Compare commits

...

32 commits

Author SHA1 Message Date
Adeeth101
b3088be1a7
Merge c549144f11 into 78d29bcf20 2025-04-16 21:41:39 +00:00
Sonal Yadav
78d29bcf20
FIX : Custom picker detect images that is already in commons (#6288)
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled
* Fix: Exclude specific text from being posted in WikidataFeedback

* Add detection logic for images already on Commons in custom picker
2025-04-15 13:53:26 +10:00
Ritika Pahwa
1a13cb3383
Add v5.3.0 to CHANGELOG.md
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-15 02:43:33 +05:30
Khushbu Khemchandani
9289dcc42c
UI enhancement Issue(#6285) (#6287)
* Exclude past locations (P585) from Nearby query

* "Send" button text should be white
2025-04-14 22:58:28 +09:00
translatewiki.net
efdc9c5548
Localisation updates from https://translatewiki.net.
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-14 14:01:44 +02:00
Khushbu Khemchandani
69b3544107
Exclude past locations (P585) from Nearby query (#6284)
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-14 10:54:29 +09:00
Ritika Pahwa
5b5aeead88
Bump up version code to 1050
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-13 13:09:19 +05:30
Prinuel
4bacac1f8b
BookmarkLocationsFragment.kt:fix android studio warnings for this file, : Eliminated three unused imports, and changed calls to object: FilePicker.HandleActivityResult to use lamda (#6283)
Co-authored-by: bethel-m <bethelcletus87@gmail.com>
2025-04-13 14:39:14 +09:00
samimshoaib01
6aeb3c07cc
ui: make recenter FAB theme-aware using Material attributes (#6281)
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-12 21:05:42 +05:30
Sonal Yadav
2c41176a6e
Mark for closed locations (P3999) in Nearby (#6273)
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled
* Exclude closed locations (P3999) from Nearby query

* feat: Show  for P3999 items (official closure)

* revert changes

* Add P3999 (date of closure) support for non-existent places

* Typo fixing

* fix-typo

* .

---------

Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
2025-04-11 18:00:47 +09:00
translatewiki.net
e3dd00bcfa
Localisation updates from https://translatewiki.net.
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-10 14:01:48 +02:00
Jason-Whitmore
262efe4d8c
ExploreMapFragment.java: fix removeMarker() to remove the correct marker (#6279)
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
Before this change, the removeMarker() method would determine the correct overlay to remove
by comparing an overlay's Place coordinates with the target BaseMarker's Place coordinates.
If two different markers had the same Place coordinates, the incorrect marker would be removed,
leading to more than one green label appearing on the screen.

After this change, the removeMarker() method now compares an overlay's title with the BaseMarker's
Place name. removeMarker() will work properly as long as all BaseMarker's Place names are unique.
Also, null checks were added to removeMarker().
2025-04-10 14:19:31 +09:00
Dmitry Brant
2eed441462
Enable EmailAuth support. (#6277)
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled
2025-04-08 20:59:28 +09:00
translatewiki.net
56fa8ceb5a
Localisation updates from https://translatewiki.net.
Some checks are pending
Android CI / Run tests and generate APK (push) Waiting to run
2025-04-07 14:01:50 +02:00
Rohit Verma
7bf9276d1a
fix: resolve IndexOutOfBounds error when removing images from top card (#6124)
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled
replace deprecated onBackPressed with onBackPressedCallback

remove unit test for deprecated onBackPressed method

remove if-check before deleting picture to prevent hiding top thumbnail card

hide the thumbnail card on fragments other than MediaDetailFragment

Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
2025-04-05 22:47:27 +09:00
Prinuel
51da9e4dd6
FooterAdapter.kt: changed enum access of FooterItem, from FooterItem.values to FooterItem.entries, this is the more efficient way of accessing Enum values as introduced in kotlin 1.9 (#6271)
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled
Co-authored-by: bethel-m <bethelcletus87@gmail.com>
2025-04-03 21:10:41 +09:00
translatewiki.net
731ff62faf
Localisation updates from https://translatewiki.net. 2025-04-03 14:01:45 +02:00
translatewiki.net
fdfd7781e9
Localisation updates from https://translatewiki.net.
Some checks failed
Android CI / Run tests and generate APK (push) Has been cancelled
2025-03-31 14:01:52 +02:00
Jason-Whitmore
6e090c8d7a
ExploreMapFragment.java: fix marker labels in Explore map fragment to display the username (#6260)
Before this change, the labels that would appear on the marker when tapped did not include
the author or username. Instead, it displayed "Unknown".

After this change, the labels now display the author name. If the author name is not
available, the username will be displayed. If both are unavailable, the default value
of "Unknown" will be displayed. To improve the readability of the text, any HTML text
is removed from the username/author.
2025-03-31 17:49:06 +09:00
Ritika Pahwa
44966645ca
Add v5.2.0 to CHANGELOG.md 2025-03-29 13:21:03 +05:30
translatewiki.net
669f3043ae
Localisation updates from https://translatewiki.net. 2025-03-27 13:01:54 +01:00
translatewiki.net
5a5e660a43
Localisation updates from https://translatewiki.net. 2025-03-24 13:01:43 +01:00
Ritika Pahwa
2e05a58e8b
Bump up version code to 1049 2025-03-22 14:07:51 +05:30
translatewiki.net
f1f4e8baff
Localisation updates from https://translatewiki.net. 2025-03-20 13:01:41 +01:00
Sonal Yadav
828f69fc46
Update Privacy Policy Link to GitHub.io (#6255)
* "moved privacy policy"

* Update PRIVACY_POLICY_URL to https://commons-app.github.io/privacy-policy as suggested

* Use BuildConfig.PRIVACY_POLICY_URL in launchPrivacyPolicy function
2025-03-20 07:21:33 +09:00
Nicolas Raoul
c549144f11
Merge branch 'main' into Languages-order-preference-#5826- 2025-01-24 16:03:00 +09:00
Nicolas Raoul
492b097ae0
Update strings.xml 2024-12-29 08:01:02 +09:00
Adeeth101
d7adc2e28d
Merge pull request #2 from commons-app/main
Update 2
2024-12-25 23:31:57 +05:30
Adith
4db858946a Fixing the nearbyController default language
Also fixes the settingsFragment to select different primary and secondary description labels.
2024-12-25 23:29:47 +05:30
Nicolas Raoul
1d479f0da7
Merge branch 'main' into Languages-order-preference-#5826- 2024-12-22 23:14:47 +09:00
Adith101
5b778af1be
Merge pull request #1 from commons-app/main
Update1
2024-12-21 18:27:42 +05:30
Adith
1fbbc072d4 Draft for MR 1 2024-12-21 18:25:25 +05:30
54 changed files with 827 additions and 230 deletions

View file

@ -1,5 +1,46 @@
# Wikimedia Commons for Android
## v5.3.0
### What's changed
* Enable EmailAuth support
* Explore map images no longer show "Unknown"
* Fix crash when removing last two images of multiupload
* Mark ❌ for closed locations (P3999) in Nearby
* Fix two pin labels staying visible at the same time in Explore map
* Refactoring and minor UI improvements
## v5.2.0
v5.2.0 boasts several new functionalities like:
* A new refresh button lets you quickly reload the Nearby map
* Bookmarks now support categories
* Improved feedback and consistency in the user interface
* Bug fixes and performance improvements
### What's changed
* Implement "Refresh" button to clear the cache and reload the Nearby map.
* `CommonsApplication` migrate to kotlin & some lint fixes.
* Revert back to MainScope for database and UI updates and make database operations thread safe.
* Hide edit options for logged-out users in Explore screen.
* Introduced a button to delete the current folder in custom selector.
* Improve Unique File Name Search.
* Migration of several modules from Java to Kotlin.
* Fix modification on bottom sheet's data when coming from Nearby Banner and clicked on other pins.
* Bug fixes and enhancement of Achievements screen.
* Show where file is being used on Commons and other wikis.
* Migrate android.media.ExifInterface to androidx.exifinterface.media.ExifInterface as android.media.ExifInterface had security flaws on older devices.
* Make dialogs modal and always show the upload icon.
* Fix unintentional deletion of subfolders and non-images by custom selector.
* Bookmark categories.
* Add pull down to refresh in the Contributions screen.
* Fix race condition and lag when loading pin details, faster overlay management.
* Show cached pins in Nearby even when internet is unavailable
Full changelog with the list of contributors: [`v5.1.2...v5.2.0`](https://github.com/commons-app/apps-android-commons/compare/v5.1.2...v5.2.0).
## v5.1.2
### What's changed

View file

@ -212,8 +212,8 @@ android {
defaultConfig {
//applicationId 'fr.free.nrw.commons'
versionCode 1048
versionName '5.2.0'
versionCode 1050
versionName '5.3.0'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion 21
@ -318,7 +318,7 @@ android {
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\""
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.org/wiki/Special:PasswordReset\""
buildConfigField "String", "PRIVACY_POLICY_URL", "\"https://github.com/commons-app/commons-app-documentation/blob/master/android/Privacy-policy.md\""
buildConfigField "String", "PRIVACY_POLICY_URL", "\"https://commons-app.github.io/privacy-policy\""
buildConfigField "String", "FILE_USAGES_BASE_URL", "\"https://commons.wikimedia.org/w/api.php?action=query&format=json&formatversion=2\""
buildConfigField "String", "ACCOUNT_TYPE", "\"fr.free.nrw.commons\""
buildConfigField "String", "CONTRIBUTION_AUTHORITY", "\"fr.free.nrw.commons.contributions.contentprovider\""
@ -355,7 +355,7 @@ android {
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\""
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/Special:PasswordReset\""
buildConfigField "String", "PRIVACY_POLICY_URL", "\"https://github.com/commons-app/commons-app-documentation/blob/master/android/Privacy-policy.md\""
buildConfigField "String", "PRIVACY_POLICY_URL", "\"https://commons-app.github.io/privacy-policy\""
buildConfigField "String", "FILE_USAGES_BASE_URL", "\"https://commons.wikimedia.org/w/api.php?action=query&format=json&formatversion=2\""
buildConfigField "String", "ACCOUNT_TYPE", "\"fr.free.nrw.commons.beta\""
buildConfigField "String", "CONTRIBUTION_AUTHORITY", "\"fr.free.nrw.commons.beta.contributions.contentprovider\""

View file

@ -65,6 +65,7 @@ class LoginActivity : AccountAuthenticatorActivity() {
private val delegate: AppCompatDelegate by lazy {
AppCompatDelegate.create(this, null)
}
private var lastLoginResult: LoginResult? = null
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -271,6 +272,7 @@ class LoginActivity : AccountAuthenticatorActivity() {
showLoggingProgressBar()
loginClient.doLogin(username,
password,
lastLoginResult,
twoFactorCode,
Locale.getDefault().language,
object : LoginCallback {
@ -280,9 +282,17 @@ class LoginActivity : AccountAuthenticatorActivity() {
onLoginSuccess(loginResult)
}
override fun twoFactorPrompt(caught: Throwable, token: String?) = runOnUiThread {
override fun twoFactorPrompt(loginResult: LoginResult, caught: Throwable, token: String?) = runOnUiThread {
Timber.d("Requesting 2FA prompt")
progressDialog!!.dismiss()
lastLoginResult = loginResult
askUserForTwoFactorAuth()
}
override fun emailAuthPrompt(loginResult: LoginResult, caught: Throwable, token: String?) {
Timber.d("Requesting email auth prompt")
progressDialog!!.dismiss()
lastLoginResult = loginResult
askUserForTwoFactorAuth()
}
@ -341,12 +351,13 @@ class LoginActivity : AccountAuthenticatorActivity() {
progressDialog!!.dismiss()
with(binding!!) {
twoFactorContainer.visibility = View.VISIBLE
twoFactorContainer.hint = getString(if (lastLoginResult is LoginResult.EmailAuthResult) R.string.email_auth_code else R.string._2fa_code)
loginTwoFactor.visibility = View.VISIBLE
loginTwoFactor.requestFocus()
}
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
showMessageAndCancelDialog(R.string.login_failed_2fa_needed)
showMessageAndCancelDialog(getString(if (lastLoginResult is LoginResult.EmailAuthResult) R.string.login_failed_email_auth_needed else R.string.login_failed_2fa_needed))
}
@VisibleForTesting

View file

@ -32,7 +32,7 @@ class CsrfTokenClient(
try {
if (retry > 0) {
// Log in explicitly
loginClient.loginBlocking(userName, password, "")
loginClient.loginBlocking(userName, password)
}
// Get CSRFToken response off the main thread.
@ -92,6 +92,8 @@ class CsrfTokenClient(
override fun failure(caught: Throwable?) = retryWithLogin(cb) { caught }
override fun twoFactorPrompt() = cb.twoFactorPrompt()
override fun emailAuthPrompt() = cb.emailAuthPrompt()
},
)
@ -165,10 +167,17 @@ class CsrfTokenClient(
}
override fun twoFactorPrompt(
loginResult: LoginResult,
caught: Throwable,
token: String?,
) = callback.twoFactorPrompt()
override fun emailAuthPrompt(
loginResult: LoginResult,
caught: Throwable,
token: String?,
) = callback.emailAuthPrompt()
// Should not happen here, but call the callback just in case.
override fun passwordResetPrompt(token: String?) = callback.failure(LoginFailedException("Logged in with temporary password."))
@ -190,6 +199,8 @@ class CsrfTokenClient(
fun failure(caught: Throwable?)
fun twoFactorPrompt()
fun emailAuthPrompt()
}
companion object {

View file

@ -4,6 +4,13 @@ interface LoginCallback {
fun success(loginResult: LoginResult)
fun twoFactorPrompt(
loginResult: LoginResult,
caught: Throwable,
token: String?,
)
fun emailAuthPrompt(
loginResult: LoginResult,
caught: Throwable,
token: String?,
)

View file

@ -1,6 +1,7 @@
package fr.free.nrw.commons.auth.login
import android.text.TextUtils
import fr.free.nrw.commons.auth.login.LoginResult.EmailAuthResult
import fr.free.nrw.commons.auth.login.LoginResult.OAuthResult
import fr.free.nrw.commons.auth.login.LoginResult.ResetPasswordResult
import fr.free.nrw.commons.wikidata.WikidataConstants.WIKIPEDIA_URL
@ -51,6 +52,7 @@ class LoginClient(
password,
null,
null,
null,
response.body()!!.query()!!.loginToken(),
userLanguage,
cb,
@ -75,6 +77,7 @@ class LoginClient(
password: String,
retypedPassword: String?,
twoFactorCode: String?,
emailAuthCode: String?,
loginToken: String?,
userLanguage: String,
cb: LoginCallback,
@ -82,7 +85,7 @@ class LoginClient(
this.userLanguage = userLanguage
loginCall =
if (twoFactorCode.isNullOrEmpty() && retypedPassword.isNullOrEmpty()) {
if (twoFactorCode.isNullOrEmpty() && emailAuthCode.isNullOrEmpty() && retypedPassword.isNullOrEmpty()) {
loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL)
} else {
loginInterface.postLogIn(
@ -90,6 +93,7 @@ class LoginClient(
password,
retypedPassword,
twoFactorCode,
emailAuthCode,
loginToken,
userLanguage,
true,
@ -112,10 +116,18 @@ class LoginClient(
when (loginResult) {
is OAuthResult ->
cb.twoFactorPrompt(
loginResult,
LoginFailedException(loginResult.message),
loginToken,
)
is EmailAuthResult ->
cb.emailAuthPrompt(
loginResult,
LoginFailedException(loginResult.message),
loginToken
)
is ResetPasswordResult -> cb.passwordResetPrompt(loginToken)
is LoginResult.Result ->
@ -147,6 +159,7 @@ class LoginClient(
fun doLogin(
username: String,
password: String,
lastLoginResult: LoginResult?,
twoFactorCode: String,
userLanguage: String,
loginCallback: LoginCallback,
@ -159,7 +172,10 @@ class LoginClient(
) = if (response.isSuccessful) {
val loginToken = response.body()?.query()?.loginToken()
loginToken?.let {
login(username, password, null, twoFactorCode, it, userLanguage, loginCallback)
login(username, password, null,
if (lastLoginResult is OAuthResult) twoFactorCode else null,
if (lastLoginResult is EmailAuthResult) twoFactorCode else null,
it, userLanguage, loginCallback)
} ?: run {
loginCallback.error(IOException("Failed to retrieve login token"))
}
@ -181,7 +197,8 @@ class LoginClient(
fun loginBlocking(
userName: String,
password: String,
twoFactorCode: String?,
twoFactorCode: String? = null,
emailAuthCode: String? = null
) {
val tokenResponse = getLoginToken().execute()
if (tokenResponse
@ -195,7 +212,7 @@ class LoginClient(
val loginToken = tokenResponse.body()?.query()?.loginToken()
val tempLoginCall =
if (twoFactorCode.isNullOrEmpty()) {
if (twoFactorCode.isNullOrEmpty() && emailAuthCode.isNullOrEmpty()) {
loginInterface.postLogIn(userName, password, loginToken, userLanguage, WIKIPEDIA_URL)
} else {
loginInterface.postLogIn(
@ -203,6 +220,7 @@ class LoginClient(
password,
null,
twoFactorCode,
emailAuthCode,
loginToken,
userLanguage,
true,
@ -214,7 +232,7 @@ class LoginClient(
val loginResult = loginResponse.toLoginResult(password) ?: throw IOException("Unexpected response when logging in.")
if ("UI" == loginResult.status) {
if (loginResult is OAuthResult) {
if (loginResult is OAuthResult || loginResult is EmailAuthResult) {
// TODO: Find a better way to boil up the warning about 2FA
throw LoginFailedException(loginResult.message)
}

View file

@ -35,7 +35,8 @@ interface LoginInterface {
@Field("password") pass: String?,
@Field("retype") retypedPass: String?,
@Field("OATHToken") twoFactorCode: String?,
@Field("logintoken") token: String?,
@Field("token") emailAuthToken: String?,
@Field("logintoken") loginToken: String?,
@Field("uselang") userLanguage: String?,
@Field("logincontinue") loginContinue: Boolean,
): Call<LoginResponse?>

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.auth.login
import com.google.gson.annotations.SerializedName
import fr.free.nrw.commons.auth.login.LoginResult.OAuthResult
import fr.free.nrw.commons.auth.login.LoginResult.EmailAuthResult
import fr.free.nrw.commons.auth.login.LoginResult.ResetPasswordResult
import fr.free.nrw.commons.auth.login.LoginResult.Result
import fr.free.nrw.commons.wikidata.mwapi.MwServiceError
@ -27,11 +28,13 @@ internal class ClientLogin {
fun toLoginResult(password: String): LoginResult {
var userMessage = message
if ("UI" == status) {
if (requests != null) {
for (req in requests) {
if ("MediaWiki\\Extension\\OATHAuth\\Auth\\TOTPAuthenticationRequest" == req.id()) {
requests?.forEach { request ->
request.id()?.let {
if (it.endsWith("TOTPAuthenticationRequest")) {
return OAuthResult(status, userName, password, message)
} else if ("MediaWiki\\Auth\\PasswordAuthenticationRequest" == req.id()) {
} else if (it.endsWith("EmailAuthAuthenticationRequest")) {
return EmailAuthResult(status, userName, password, message)
} else if (it.endsWith("PasswordAuthenticationRequest")) {
return ResetPasswordResult(status, userName, password, message)
}
}
@ -49,7 +52,7 @@ internal class Request {
private val required: String? = null
private val provider: String? = null
private val account: String? = null
private val fields: Map<String, RequestField>? = null
internal val fields: Map<String, RequestField>? = null
fun id(): String? = id
}
@ -57,5 +60,5 @@ internal class Request {
internal class RequestField {
private val type: String? = null
private val label: String? = null
private val help: String? = null
internal val help: String? = null
}

View file

@ -24,6 +24,13 @@ sealed class LoginResult(
message: String?,
) : LoginResult(status, userName, password, message)
class EmailAuthResult(
status: String,
userName: String?,
password: String?,
message: String?,
) : LoginResult(status, userName, password, message)
class ResetPasswordResult(
status: String,
userName: String?,

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.bookmarks.locations
import android.Manifest.permission
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -9,15 +8,12 @@ import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.android.support.DaggerFragment
import fr.free.nrw.commons.R
import fr.free.nrw.commons.contributions.ContributionController
import fr.free.nrw.commons.databinding.FragmentBookmarksLocationsBinding
import fr.free.nrw.commons.filepicker.FilePicker
import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.nearby.fragments.CommonPlaceClickActions
import fr.free.nrw.commons.nearby.fragments.PlaceAdapter
@ -41,33 +37,27 @@ class BookmarkLocationsFragment : DaggerFragment() {
private val cameraPickLauncherForResult =
registerForActivityResult(StartActivityForResult()) { result ->
contributionController.handleActivityResultWithCallback(
requireActivity(),
object: FilePicker.HandleActivityResult {
override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) {
contributionController.onPictureReturnedFromCamera(
result,
requireActivity(),
callbacks
)
}
}
)
requireActivity()
) { callbacks ->
contributionController.onPictureReturnedFromCamera(
result,
requireActivity(),
callbacks
)
}
}
private val galleryPickLauncherForResult =
registerForActivityResult(StartActivityForResult()) { result ->
contributionController.handleActivityResultWithCallback(
requireActivity(),
object: FilePicker.HandleActivityResult {
override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) {
contributionController.onPictureReturnedFromGallery(
result,
requireActivity(),
callbacks
)
}
}
)
requireActivity()
) { callbacks ->
contributionController.onPictureReturnedFromGallery(
result,
requireActivity(),
callbacks
)
}
}
companion object {

View file

@ -26,6 +26,7 @@ import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.TreeMap
import kotlin.collections.ArrayList
@ -342,45 +343,36 @@ class ImageAdapter(
numberOfSelectedImagesMarkedAsNotForUpload--
}
notifyItemChanged(position, ImageUnselected())
// Getting index from all images index when switch is on
val indexes =
if (showAlreadyActionedImages) {
ImageHelper.getIndexList(selectedImages, images)
// Getting index from actionable images when switch is off
} else {
ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values))
}
for (index in indexes) {
notifyItemChanged(index, ImageSelectedOrUpdated())
}
} else {
val image = images[position]
scope.launch(ioDispatcher) {
val imageSHA1 = imageLoader.getSHA1(image, defaultDispatcher)
withContext(Dispatchers.Main) {
if (holder.isItemUploaded()) {
Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show()
} else {
if (holder.isItemNotForUpload()) {
numberOfSelectedImagesMarkedAsNotForUpload++
}
// Getting index from all images index when switch is on
val indexes: ArrayList<Int> =
if (showAlreadyActionedImages) {
selectedImages.add(images[position])
ImageHelper.getIndexList(selectedImages, images)
// Getting index from actionable images when switch is off
} else {
selectedImages.add(ArrayList(actionableImagesMap.values)[position])
ImageHelper.getIndexList(selectedImages, ArrayList(actionableImagesMap.values))
return@withContext
}
for (index in indexes) {
notifyItemChanged(index, ImageSelectedOrUpdated())
if (imageSHA1.isNotEmpty() && imageLoader.getFromUploaded(imageSHA1) != null) {
holder.itemUploaded()
Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show()
return@withContext
}
if (!holder.isItemUploaded() && imageSHA1.isNotEmpty() && imageLoader.getFromUploaded(imageSHA1) != null) {
Toast.makeText(context, R.string.custom_selector_already_uploaded_image_text, Toast.LENGTH_SHORT).show()
}
if (holder.isItemNotForUpload()) {
numberOfSelectedImagesMarkedAsNotForUpload++
}
selectedImages.add(image)
notifyItemChanged(position, ImageSelectedOrUpdated())
imageSelectListener.onSelectedImagesChanged(selectedImages, numberOfSelectedImagesMarkedAsNotForUpload)
}
}
}
imageSelectListener.onSelectedImagesChanged(selectedImages, numberOfSelectedImagesMarkedAsNotForUpload)
}
/**

View file

@ -638,17 +638,20 @@ class CustomSelectorActivity :
finishPickImages(arrayListOf())
return
}
var i = 0
while (i < selectedImages.size) {
val path = selectedImages[i].path
val file = File(path)
if (!file.exists()) {
selectedImages.removeAt(i)
i--
scope.launch(ioDispatcher) {
val uniqueImages = selectedImages.distinctBy { image ->
CustomSelectorUtils.getImageSHA1(
image.uri,
ioDispatcher,
fileUtilsWrapper,
contentResolver
)
}
withContext(Dispatchers.Main) {
finishPickImages(ArrayList(uniqueImages))
}
i++
}
finishPickImages(selectedImages)
}
/**

View file

@ -708,8 +708,17 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
GeoPoint point = new GeoPoint(
nearbyBaseMarker.getPlace().location.getLatitude(),
nearbyBaseMarker.getPlace().location.getLongitude());
OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, null,
point);
Media markerMedia = this.getMediaFromImageURL(nearbyBaseMarker.getPlace().pic);
String authorUser = null;
if (markerMedia != null) {
authorUser = markerMedia.getAuthorOrUser();
// HTML text is sometimes part of the author string and needs to be removed
authorUser = Html.fromHtml(authorUser, Html.FROM_HTML_MODE_LEGACY).toString();
}
OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name,
authorUser, point);
item.setMarker(d);
items.add(item);
ItemizedOverlayWithFocus overlay = new ItemizedOverlayWithFocus(items,
@ -740,13 +749,37 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
}
}
/**
* Retrieves the specific Media object from the mediaList field.
* @param url The specific Media's image URL.
* @return The Media object that matches the URL or null if it could not be found.
*/
private Media getMediaFromImageURL(String url) {
if (mediaList == null || url == null) {
return null;
}
for (int i = 0; i < mediaList.size(); i++) {
if (mediaList.get(i) != null && mediaList.get(i).getImageUrl() != null
&& mediaList.get(i).getImageUrl().equals(url)) {
return mediaList.get(i);
}
}
return null;
}
/**
* Removes a marker from the map based on the specified NearbyBaseMarker.
*
* @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be removed.
*/
private void removeMarker(BaseMarker nearbyBaseMarker) {
Place place = nearbyBaseMarker.getPlace();
if (nearbyBaseMarker == null || nearbyBaseMarker.getPlace().getName() == null) {
return;
}
String target = nearbyBaseMarker.getPlace().getName();
List<Overlay> overlays = binding.mapView.getOverlays();
ItemizedOverlayWithFocus item;
@ -755,8 +788,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
item = (ItemizedOverlayWithFocus) overlays.get(i);
OverlayItem overlayItem = item.getItem(0);
if (place.location.getLatitude() == overlayItem.getPoint().getLatitude()
&& place.location.getLongitude() == overlayItem.getPoint().getLongitude()) {
if (overlayItem.getTitle().equals(target)) {
binding.mapView.getOverlays().remove(i);
binding.mapView.invalidate();
break;

View file

@ -32,7 +32,7 @@ class FooterAdapter(
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
) = when (FooterItem.values()[viewType]) {
) = when (FooterItem.entries[viewType]) {
FooterItem.LoadingItem ->
LoadingViewHolder(
parent.inflate(R.layout.list_item_progress),

View file

@ -484,19 +484,65 @@ class OkHttpJsonApiClient @Inject constructor(
@Throws(IOException::class)
fun getPlaces(
placeList: List<Place>, language: String
placeList: List<Place>, primaryLanguage: String, secondaryLanguages: String
): List<Place>? {
val wikidataQuery = FileUtils.readFromResource("/queries/query_for_item.rq")
// Split the secondary languages string into an array to use in fallback queries
val secondaryLanguagesArray = secondaryLanguages.split(",\\s*".toRegex())
// Prepare the Wikidata entity IDs (QIDs) for each place in the list
var qids = ""
for (place in placeList) {
qids += """
${"wd:" + place.wikiDataEntityId}"""
qids += "\nwd:${place.wikiDataEntityId}"
}
// Build fallback descriptions for secondary languages in case the primary language is unavailable
val fallBackDescription = StringBuilder()
secondaryLanguagesArray.forEachIndexed { index, lang ->
fallBackDescription.append("OPTIONAL {?item schema:description ?itemDescriptionPreferredLanguage_")
.append(index + 1)
.append(". FILTER (lang(?itemDescriptionPreferredLanguage_")
.append(index + 1)
.append(") = \"")
.append(lang)
.append("\")}\n")
}
// Build fallback labels for secondary languages
val fallbackLabel = StringBuilder()
secondaryLanguagesArray.forEachIndexed { index, lang ->
fallbackLabel.append("OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage_")
.append(index + 1)
.append(". FILTER (lang(?itemLabelPreferredLanguage_")
.append(index + 1)
.append(") = \"")
.append(lang)
.append("\")}\n")
}
// Build fallback class labels for secondary languages
val fallbackClassLabel = StringBuilder()
secondaryLanguagesArray.forEachIndexed { index, lang ->
fallbackClassLabel.append("OPTIONAL {?class rdfs:label ?classLabelPreferredLanguage_")
.append(index + 1)
.append(". FILTER (lang(?classLabelPreferredLanguage_")
.append(index + 1)
.append(") = \"")
.append(lang)
.append("\")}\n")
}
// Replace placeholders in the query with actual data: QIDs, language codes, and fallback options
val query = wikidataQuery
.replace("\${ENTITY}", qids)
.replace("\${LANG}", language)
val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!!
.newBuilder()
.replace("\${LANG}", primaryLanguage)
.replace("\${SECONDARYDESCRIPTION}", fallBackDescription.toString())
.replace("\${SECONDARYLABEL}", fallbackLabel.toString())
.replace("\${SECONDARYCLASSLABEL}", fallbackClassLabel.toString())
// Build the URL for the SparQL query with the formatted query string
val urlBuilder = sparqlQueryUrl.toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("query", query)
.addQueryParameter("format", "json")
@ -514,11 +560,12 @@ ${"wd:" + place.wikiDataEntityId}"""
}
return places
} else {
throw IOException("Unexpected response code: " + response.code)
throw IOException("Unexpected response code: ${response.code}")
}
}
}
@Throws(Exception::class)
fun getPlacesAsKML(leftLatLng: LatLng, rightLatLng: LatLng): String? {
var kmlString = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>

View file

@ -7,7 +7,9 @@ import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.BaseMarker;
import fr.free.nrw.commons.MapController;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.settings.Prefs;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -16,6 +18,7 @@ import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber;
public class NearbyController extends MapController {
@ -34,6 +37,10 @@ public class NearbyController extends MapController {
this.nearbyPlaces = nearbyPlaces;
}
@Inject
@Named("default_preferences")
JsonKvStore defaultKvStore;
/**
* Prepares Place list to make their distance information update later.
@ -139,7 +146,9 @@ public class NearbyController extends MapController {
* @throws Exception If an error occurs during the retrieval process.
*/
public List<Place> getPlaces(List<Place> placeList) throws Exception {
return nearbyPlaces.getPlaces(placeList, Locale.getDefault().getLanguage());
String secondaryLanguages = defaultKvStore.getString(Prefs.SECONDARY_LANGUAGES, "");
String primaryLanguage = defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, "");
return nearbyPlaces.getPlaces(placeList, primaryLanguage, secondaryLanguages);
}
public static LatLng calculateNorthEast(double latitude, double longitude, double distance) {

View file

@ -191,9 +191,9 @@ public class NearbyPlaces {
* @throws Exception If an error occurs during the retrieval process.
*/
public List<Place> getPlaces(final List<Place> placeList,
final String lang) throws Exception {
final String lang, final String lang2) throws Exception {
return okHttpJsonApiClient
.getPlaces(placeList, lang);
.getPlaces(placeList, lang, lang2);
}
/**

View file

@ -153,7 +153,10 @@ public class Place implements Parcelable {
.build(),
item.getPic().getValue(),
// Checking if the place exists or not
(item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == ""), entityId);
(item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == "")
&& (item.getDateOfOfficialClosure().getValue() == "")
&& (item.getPointInTime().getValue()==""),
entityId);
}
/**

View file

@ -103,4 +103,4 @@ class WikidataFeedback : BaseActivity() {
onBackPressed()
return true
}
}
}

View file

@ -18,6 +18,8 @@ class NearbyResultItem(
@field:SerializedName("description") private val description: ResultTuple?,
@field:SerializedName("endTime") private val endTime: ResultTuple?,
@field:SerializedName("monument") private val monument: ResultTuple?,
@field:SerializedName("dateOfOfficialClosure") private val dateOfOfficialClosure: ResultTuple?,
@field:SerializedName("pointInTime") private val pointInTime: ResultTuple?,
) {
fun getItem(): ResultTuple = item ?: ResultTuple()
@ -41,6 +43,8 @@ class NearbyResultItem(
fun getDestroyed(): ResultTuple = destroyed ?: ResultTuple()
fun getDateOfOfficialClosure(): ResultTuple = dateOfOfficialClosure ?: ResultTuple()
fun getDescription(): ResultTuple = description ?: ResultTuple()
fun getEndTime(): ResultTuple = endTime ?: ResultTuple()
@ -48,4 +52,7 @@ class NearbyResultItem(
fun getAddress(): String = address?.value ?: ""
fun getMonument(): ResultTuple? = monument
fun getPointInTime(): ResultTuple = pointInTime ?: ResultTuple()
}

View file

@ -0,0 +1,77 @@
package fr.free.nrw.commons.recentlanguages
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import fr.free.nrw.commons.R
import fr.free.nrw.commons.databinding.RowItemLanguagesSpinnerBinding
import fr.free.nrw.commons.utils.LangCodeUtils
import org.apache.commons.lang3.StringUtils
import java.util.HashMap
/**
* Array adapter for saved languages
*/
class SavedLanguagesAdapter constructor(
context: Context,
var savedLanguages: List<Language>, // List of saved languages
private val selectedLanguages: HashMap<*, String>, // Selected languages map
) : ArrayAdapter<String?>(context, R.layout.row_item_languages_spinner) {
/**
* Selected language code in SavedLanguagesAdapter
* Used for marking selected ones
*/
var selectedLangCode = ""
override fun isEnabled(position: Int) =
savedLanguages[position].languageCode.let {
it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode
}
override fun getCount() = savedLanguages.size
override fun getView(
position: Int,
convertView: View?,
parent: ViewGroup,
): View {
val binding: RowItemLanguagesSpinnerBinding
var rowView = convertView
if (rowView == null) {
val layoutInflater =
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
binding = RowItemLanguagesSpinnerBinding.inflate(layoutInflater, parent, false)
rowView = binding.root
} else {
binding = RowItemLanguagesSpinnerBinding.bind(rowView)
}
val languageCode = savedLanguages[position].languageCode
val languageName = savedLanguages[position].languageName
binding.tvLanguage.let {
it.isEnabled = isEnabled(position)
if (languageCode.isEmpty()) {
it.text = StringUtils.capitalize(languageName)
it.textAlignment = View.TEXT_ALIGNMENT_CENTER
} else {
it.text =
"${StringUtils.capitalize(languageName)}" +
" [${LangCodeUtils.fixLanguageCode(languageCode)}]"
}
}
return rowView
}
/**
* Provides code of a language from saved languages for a specific position
*/
fun getLanguageCode(position: Int): String = savedLanguages[position].languageCode
/**
* Provides name of a language from saved languages for a specific position
*/
fun getLanguageName(position: Int): String = savedLanguages[position].languageName
}

View file

@ -5,6 +5,7 @@ object Prefs {
const val MANAGED_EXIF_TAGS = "managed_exif_tags"
const val VANISHED_ACCOUNT = "vanishAccount"
const val DESCRIPTION_LANGUAGE = "languageDescription"
const val SECONDARY_LANGUAGES = "secondaryLanguages"
const val APP_UI_LANGUAGE = "appUiLanguage"
const val KEY_THEME_VALUE = "appThemePref"

View file

@ -16,6 +16,7 @@ import android.widget.Button
import android.widget.EditText
import android.widget.ListView
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
@ -78,6 +79,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var vanishAccountPreference: Preference? = null
private var themeListPreference: ListPreference? = null
private var descriptionLanguageListPreference: Preference? = null
private var descriptionSecondaryLanguagesListPreference: Preference? = null
private var appUiLanguageListPreference: Preference? = null
private var showDeletionButtonPreference: Preference? = null
private var keyLanguageListPreference: String? = null
@ -229,6 +231,12 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
descriptionSecondaryLanguagesListPreference = findPreference("descriptionSecondaryLanguagesPref")
descriptionSecondaryLanguagesListPreference?.setOnPreferenceClickListener {
prepareSecondaryLanguagesDialog()
true
}
showDeletionButtonPreference = findPreference("displayDeletionButton")
showDeletionButtonPreference?.setOnPreferenceChangeListener { _, newValue ->
val isEnabled = newValue as Boolean
@ -324,6 +332,91 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
private fun prepareSecondaryLanguagesDialog() {
val languageCode = getCurrentLanguageCode("descriptionSecondaryLanguagesPref")
val defaultCode = getCurrentLanguageCode("descriptionDefaultLanguagePref")
val selectedLanguages = hashMapOf<Int, String>()
var deflocale = Locale.getDefault()
if (defaultCode != null){
deflocale = createLocale(defaultCode)
}
languageCode?.let {
selectedLanguages[0] = deflocale.language
}
val savedLanguages = arrayListOf<Language>()
languageCode?.split(",\\s*".toRegex())?.forEach { code ->
if (code != deflocale.language) {
val locale = Locale(code)
savedLanguages.add(Language(locale.displayLanguage, code))
}
}
val dialog = Dialog(requireActivity())
dialog.setContentView(R.layout.dialog_select_secondary_languages)
dialog.setCanceledOnTouchOutside(true)
dialog.window?.setLayout(
(resources.displayMetrics.widthPixels * 0.90).toInt(),
(resources.displayMetrics.heightPixels * 0.90).toInt()
)
dialog.show()
val editText: EditText = dialog.findViewById(R.id.search_language)
val listView: ListView = dialog.findViewById(R.id.language_list)
val savedLanguageListView: ListView = dialog.findViewById(R.id.language_history_list)
val separator: View = dialog.findViewById(R.id.separator)
updateSavedLanguages(savedLanguageListView, savedLanguages, selectedLanguages)
val languagesAdapter = LanguagesAdapter(requireActivity(), selectedLanguages)
listView.adapter = languagesAdapter
editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) {
languagesAdapter.filter.filter(charSequence)
}
override fun afterTextChanged(editable: Editable?) {}
})
savedLanguageListView.setOnItemClickListener { _, _, position, _ ->
savedLanguages.removeAt(position)
updateSavedLanguages(savedLanguageListView, savedLanguages, selectedLanguages)
saveLanguageValue(
savedLanguages.joinToString(", ") { it.languageCode },
"descriptionSecondaryLanguagesPref"
)
}
listView.setOnItemClickListener { _, _, position, _ ->
val selectedLanguageCode = languagesAdapter.getLanguageCode(position)
val selectedLanguageName = languagesAdapter.getLanguageName(position)
if (savedLanguages.any { it.languageCode == selectedLanguageCode }) {
Toast.makeText(requireActivity(), "Language already selected", Toast.LENGTH_SHORT).show()
return@setOnItemClickListener
}
savedLanguages.add(Language(selectedLanguageName, selectedLanguageCode))
updateSavedLanguages(savedLanguageListView, savedLanguages, selectedLanguages)
saveLanguageValue(
savedLanguages.joinToString(", ") { it.languageCode },
"descriptionSecondaryLanguagesPref"
)
}
dialog.setOnDismissListener {
saveLanguageValue(
savedLanguages.joinToString(", ") { it.languageCode },
"descriptionSecondaryLanguagesPref"
)
}
}
/**
* Prepare and Show language selection dialog box
* Uses previously saved language if there is any, if not uses phone locale as initial language.
@ -531,6 +624,16 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
// Helper function to update saved languages
private fun updateSavedLanguages(
savedLanguageListView: ListView,
savedLanguages: List<Language>,
selectedLanguages: HashMap<Int, String>
) {
val savedLanguagesAdapter = RecentLanguagesAdapter(requireActivity(), savedLanguages, selectedLanguages)
savedLanguageListView.adapter = savedLanguagesAdapter
}
/**
* Save userSelected language in List Preference
* @param userSelectedValue
@ -540,6 +643,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
when (preferenceKey) {
"appUiDefaultLanguagePref" -> defaultKvStore.putString(Prefs.APP_UI_LANGUAGE, userSelectedValue)
"descriptionDefaultLanguagePref" -> defaultKvStore.putString(Prefs.DESCRIPTION_LANGUAGE, userSelectedValue)
"descriptionSecondaryLanguagesPref" -> defaultKvStore.putString(Prefs.SECONDARY_LANGUAGES, userSelectedValue)
}
}
@ -556,6 +660,9 @@ class SettingsFragment : PreferenceFragmentCompat() {
"descriptionDefaultLanguagePref" -> defaultKvStore.getString(
Prefs.DESCRIPTION_LANGUAGE, ""
)
"descriptionSecondaryLanguagesPref" -> defaultKvStore.getString(
Prefs.SECONDARY_LANGUAGES, ""
)
else -> null
}
}

View file

@ -14,6 +14,7 @@ import android.os.Bundle
import android.provider.Settings
import android.view.View
import android.widget.CheckBox
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
@ -122,7 +123,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
/**
* Set the value of the showPermissionDialog variable.
*
* @param showPermissionsDialog `true` to indicate to show
* @property isShowPermissionsDialog `true` to indicate to show
* Permissions Dialog if permissions are missing, `false` otherwise.
*/
/**
@ -166,6 +167,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
private var _binding: ActivityUploadBinding? = null
private val binding: ActivityUploadBinding get() = _binding!!
private lateinit var onBackPressedCallback: OnBackPressedCallback
@SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -173,6 +176,23 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
_binding = ActivityUploadBinding.inflate(layoutInflater)
setContentView(binding.root)
// Overrides the back button to make sure the user is prepared to lose their progress
onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
showAlertDialog(
this@UploadActivity,
getString(R.string.back_button_warning),
getString(R.string.back_button_warning_desc),
getString(R.string.back_button_continue),
getString(R.string.back_button_warning),
null
) {
finish()
}
}
}
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
/*
If Configuration of device is changed then get the new fragments
created by the system and populate the fragments ArrayList
@ -187,7 +207,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
}
init()
binding.rlContainerTitle.setOnClickListener { v: View? -> onRlContainerTitleClicked() }
binding.rlContainerTitle.setOnClickListener { _: View? -> onRlContainerTitleClicked() }
nearbyPopupAnswers = mutableMapOf()
//getting the current dpi of the device and if it is less than 320dp i.e. overlapping
//threshold, thumbnails automatically minimizes
@ -201,7 +221,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
}
locationManager!!.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
locationManager!!.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
store = BasicKvStore(this, storeNameForCurrentUploadImagesSize).apply {
store = BasicKvStore(this, STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE).apply {
clearAll()
}
checkStoragePermissions()
@ -241,7 +261,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
override fun onPageSelected(position: Int) {
currentSelectedPosition = position
if (position >= uploadableFiles!!.size) {
if (position >= uploadableFiles.size) {
binding.cvContainerTopCard.visibility = View.GONE
} else {
thumbnailsAdapter!!.notifyDataSetChanged()
@ -274,7 +294,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.filter { result: Boolean? -> result!! }
.subscribe { result: Boolean? ->
.subscribe { _: Boolean? ->
showAlertDialog(
this,
getString(R.string.block_notification_title),
@ -284,7 +304,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
})
}
fun checkStoragePermissions() {
private fun checkStoragePermissions() {
// Check if all required permissions are granted
val hasAllPermissions = hasPermission(this, PERMISSIONS_STORAGE)
val hasPartialAccess = hasPartialAccess(this)
@ -355,7 +375,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
showLongToast(this, messageResourceId)
}
override fun getUploadableFiles(): List<UploadableFile>? {
override fun getUploadableFiles(): List<UploadableFile> {
return uploadableFiles
}
@ -367,6 +387,14 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
override fun onUploadMediaDeleted(index: Int) {
fragments!!.removeAt(index) //Remove the corresponding fragment
uploadableFiles.removeAt(index) //Remove the files from the list
val isMediaDetailFragment = fragments!!.getOrNull(currentSelectedPosition)?.let {
it is UploadMediaDetailFragment
} ?: false
if(!isMediaDetailFragment) {
// Should hide the top card current fragment is not the media detail fragment
showHideTopCard(false)
}
thumbnailsAdapter!!.notifyItemRemoved(index) //Notify the thumbnails adapter
uploadImagesAdapter!!.notifyDataSetChanged() //Notify the ViewPager
}
@ -375,8 +403,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
binding.tvTopCardTitle.text = resources
.getQuantityString(
R.plurals.upload_count_title,
uploadableFiles!!.size,
uploadableFiles!!.size
uploadableFiles.size,
uploadableFiles.size
)
}
@ -444,15 +472,16 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
receiveInternalSharedItems()
}
if (uploadableFiles == null || uploadableFiles!!.isEmpty()) {
if (uploadableFiles.isEmpty()) {
handleNullMedia()
} else {
//Show thumbnails
if (uploadableFiles!!.size > 1) {
if (!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")) { //If there is only file, no need to show the image thumbnails
if (uploadableFiles.size > 1) {
if (!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")) {
// If there is only file, no need to show the image thumbnails
showAlertDialogForCategories()
}
if (uploadableFiles!!.size > 3 &&
if (uploadableFiles.size > 3 &&
!defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")
) {
showAlertForBattery()
@ -464,8 +493,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
binding.tvTopCardTitle.text = resources
.getQuantityString(
R.plurals.upload_count_title,
uploadableFiles!!.size,
uploadableFiles!!.size
uploadableFiles.size,
uploadableFiles.size
)
@ -474,7 +503,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
}
for (uploadableFile in uploadableFiles!!) {
for (uploadableFile in uploadableFiles) {
val uploadMediaDetailFragment = UploadMediaDetailFragment()
if (!uploadIsOfAPlace) {
@ -497,8 +526,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
object : UploadMediaDetailFragmentCallback {
override fun deletePictureAtIndex(index: Int) {
store!!.putInt(
keyForCurrentUploadImagesSize,
(store!!.getInt(keyForCurrentUploadImagesSize) - 1)
KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE,
(store!!.getInt(KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE) - 1)
)
presenter!!.deletePictureAtIndex(index)
}
@ -576,11 +605,11 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
fragments!!.add(mediaLicenseFragment!!)
} else {
for (i in 1 until fragments!!.size) {
fragments!![i]!!.callback = object : UploadBaseFragment.Callback {
fragments!![i].callback = object : UploadBaseFragment.Callback {
override fun onNextButtonClicked(index: Int) {
if (index < fragments!!.size - 1) {
binding.vpUpload.setCurrentItem(index + 1, false)
fragments!![index + 1]!!.onBecameVisible()
fragments!![index + 1].onBecameVisible()
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset(
if ((index > 0)) index - 1 else 0,
@ -594,7 +623,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
override fun onPreviousButtonClicked(index: Int) {
if (index != 0) {
binding.vpUpload.setCurrentItem(index - 1, true)
fragments!![index - 1]!!.onBecameVisible()
fragments!![index - 1].onBecameVisible()
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset(
if ((index > 3)) index - 2 else 0,
@ -632,11 +661,12 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
binding.vpUpload.offscreenPageLimit = fragments!!.size
}
// Saving size of uploadableFiles
store!!.putInt(keyForCurrentUploadImagesSize, uploadableFiles!!.size)
store!!.putInt(KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE, uploadableFiles.size)
}
/**
* Changes current image when one image upload is cancelled, to highlight next image in the top thumbnail.
* Changes current image when one image upload is cancelled, to highlight next image in the top
* thumbnail.
* Fixes: [Issue](https://github.com/commons-app/apps-android-commons/issues/5511)
*
* @param index Index of image to be removed
@ -771,7 +801,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
override fun onNextButtonClicked(index: Int) {
if (index < fragments!!.size - 1) {
binding.vpUpload.setCurrentItem(index + 1, false)
fragments!![index + 1]!!.onBecameVisible()
fragments!![index + 1].onBecameVisible()
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset(if ((index > 0)) index - 1 else 0, 0)
if (index < fragments!!.size - 4) {
@ -786,10 +816,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
override fun onPreviousButtonClicked(index: Int) {
if (index != 0) {
binding.vpUpload.setCurrentItem(index - 1, true)
fragments!![index - 1]!!.onBecameVisible()
fragments!![index - 1].onBecameVisible()
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset(if ((index > 3)) index - 2 else 0, 0)
if ((index != 1) && ((index - 1) < uploadableFiles!!.size)) {
if ((index != 1) && ((index - 1) < uploadableFiles.size)) {
// Shows the top card if it was hidden because of the last image being deleted and
// now the user has hit previous button to go back to the media details
showHideTopCard(true)
@ -797,7 +827,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
}
}
override fun onThumbnailDeleted(position: Int) = presenter!!.deletePictureAtIndex(position)
override fun onThumbnailDeleted(position: Int) {
presenter!!.deletePictureAtIndex(position)
thumbnailsAdapter?.notifyDataSetChanged()
}
/**
* The adapter used to show image upload intermediate fragments
@ -824,11 +857,11 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
}
fun onRlContainerTitleClicked() {
private fun onRlContainerTitleClicked() {
binding.rvThumbnails.visibility =
if (isTitleExpanded) View.GONE else View.VISIBLE
isTitleExpanded = !isTitleExpanded
binding.ibToggleTopCard.rotation = binding.ibToggleTopCard.rotation + 180
binding.ibToggleTopCard.rotation += 180
}
override fun onDestroy() {
@ -845,21 +878,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
if (uploadCategoriesFragment != null) {
uploadCategoriesFragment!!.callback = null
}
}
/**
* Overrides the back button to make sure the user is prepared to lose their progress
*/
@SuppressLint("MissingSuperCall")
override fun onBackPressed() {
showAlertDialog(
this,
getString(R.string.back_button_warning),
getString(R.string.back_button_warning_desc),
getString(R.string.back_button_continue),
getString(R.string.back_button_warning),
null
) { finish() }
onBackPressedCallback.remove()
}
/**
@ -879,7 +898,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
.setView(view)
.setTitle(getString(R.string.multiple_files_depiction_header))
.setMessage(getString(R.string.multiple_files_depiction))
.setPositiveButton("OK") { dialog: DialogInterface?, which: Int ->
.setPositiveButton("OK") { _: DialogInterface?, _: Int ->
if (checkBox.isChecked) {
// Save the user's choice to not show the dialog again
defaultKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", true)
@ -913,14 +932,14 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
getString(R.string.cancel),
{
/* Since opening the right settings page might be device dependent, using
https://github.com/WaseemSabir/BatteryPermissionHelper
directly appeared like a promising idea.
However, this simply closed the popup and did not make
the settings page appear on a Pixel as well as a Xiaomi device.
Used the standard intent instead of using this library as
it shows a list of all the apps on the device and allows users to
turn battery optimisation off.
*/
https://github.com/WaseemSabir/BatteryPermissionHelper
directly appeared like a promising idea.
However, this simply closed the popup and did not make
the settings page appear on a Pixel as well as a Xiaomi device.
Used the standard intent instead of using this library as
it shows a list of all the apps on the device and allows users to
turn battery optimisation off.
*/
val batteryOptimisationSettingsIntent = Intent(
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
)
@ -958,7 +977,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
Also, location information is discarded if the difference between
current location and location recorded just before capturing the image
is greater than 100 meters */
if (isLocationTagUnchecked || locationDifference > 100 || !defaultKvStore.getBoolean("inAppCameraLocationPref")
if (isLocationTagUnchecked || locationDifference > 100
|| !defaultKvStore.getBoolean("inAppCameraLocationPref")
|| !isInAppCameraUpload
) {
currLocation = null
@ -979,8 +999,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
@JvmField
var nearbyPopupAnswers: MutableMap<Place, Boolean>? = null
const val keyForCurrentUploadImagesSize: String = "CurrentUploadImagesSize"
const val storeNameForCurrentUploadImagesSize: String = "CurrentUploadImageQualities"
const val KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE: String = "CurrentUploadImagesSize"
const val STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE: String = "CurrentUploadImageQualities"
/**
* Sets the flag indicating whether the upload is of a specific place.

View file

@ -146,34 +146,31 @@ class UploadPresenter @Inject internal constructor(
override fun deletePictureAtIndex(index: Int) {
val uploadableFiles = view.getUploadableFiles()
if (index == uploadableFiles!!.size - 1) {
// If the next fragment to be shown is not one of the MediaDetailsFragment
// lets hide the top card so that it doesn't appear on the other fragments
view.showHideTopCard(false)
}
view.setImageCancelled(true)
repository.deletePicture(uploadableFiles[index].getFilePath())
if (uploadableFiles.size == 1) {
view.showMessage(R.string.upload_cancelled)
view.finish()
return
}
presenter.updateImageQualitiesJSON(uploadableFiles.size, index)
view.onUploadMediaDeleted(index)
if (index != uploadableFiles.size && index != 0) {
// if the deleted image was not the last item to be uploaded, check quality of next
repository.getUploadItem(index)?.let {
presenter.checkImageQuality(it, index)
uploadableFiles?.let {
view.setImageCancelled(true)
repository.deletePicture(uploadableFiles[index].getFilePath())
if (uploadableFiles.size == 1) {
view.showMessage(R.string.upload_cancelled)
view.finish()
return
}
}
if (uploadableFiles.size < 2) {
view.showHideTopCard(false)
}
presenter.updateImageQualitiesJSON(uploadableFiles.size, index)
view.onUploadMediaDeleted(index)
if (index != uploadableFiles.size && index != 0) {
// if the deleted image was not the last item to be uploaded, check quality of next
repository.getUploadItem(index)?.let {
presenter.checkImageQuality(it, index)
}
}
//In case lets update the number of uploadable media
view.updateTopCardTitle()
if (uploadableFiles.size < 2) {
view.showHideTopCard(false)
}
//In case lets update the number of uploadable media
view.updateTopCardTitle()
}
}
override fun onAttachView(view: UploadContract.View) {

View file

@ -532,7 +532,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
basicKvStore!!.putBoolean(keyForShowingAlertDialog, false)
if (isInternetConnectionEstablished(requireActivity())) {
val sizeOfUploads = basicKvStore!!.getInt(
UploadActivity.keyForCurrentUploadImagesSize
UploadActivity.KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE
)
for (i in indexOfFragment until sizeOfUploads) {
presenter.getImageQuality(

View file

@ -310,7 +310,7 @@ class UploadMediaPresenter @Inject constructor(
private fun storeImageQuality(
imageResult: Int, uploadItemIndex: Int, activity: Activity, uploadItem: UploadItem
) {
val store = BasicKvStore(activity, UploadActivity.storeNameForCurrentUploadImagesSize)
val store = BasicKvStore(activity, UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE)
val value = store.getString(UPLOAD_QUALITIES_KEY, null)
try {
val jsonObject = value.asJsonObject().apply {
@ -339,8 +339,10 @@ class UploadMediaPresenter @Inject constructor(
*/
override fun checkImageQuality(uploadItem: UploadItem, index: Int) {
if ((uploadItem.imageQuality != IMAGE_OK) && (uploadItem.imageQuality != IMAGE_KEEP)) {
val value = basicKvStoreFactory?.let { it(UploadActivity.storeNameForCurrentUploadImagesSize) }
val value = basicKvStoreFactory?.let { it(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) }
?.getString(UPLOAD_QUALITIES_KEY, null)
try {
val imageQuality = value.asJsonObject()["UploadItem$index"] as Int
view.showProgress(false)
@ -363,8 +365,9 @@ class UploadMediaPresenter @Inject constructor(
* @param index Index of the UploadItem which was deleted
*/
override fun updateImageQualitiesJSON(size: Int, index: Int) {
val value = basicKvStoreFactory?.let { it(UploadActivity.storeNameForCurrentUploadImagesSize) }
val value = basicKvStoreFactory?.let { it(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) }
?.getString(UPLOAD_QUALITIES_KEY, null)
try {
val jsonObject = value.asJsonObject().apply {
for (i in index until (size - 1)) {
@ -372,7 +375,8 @@ class UploadMediaPresenter @Inject constructor(
}
remove("UploadItem" + (size - 1))
}
basicKvStoreFactory?.let { it(UploadActivity.storeNameForCurrentUploadImagesSize) }
basicKvStoreFactory?.let { it(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) }
?.putString(UPLOAD_QUALITIES_KEY, jsonObject.toString())
} catch (e: Exception) {
Timber.e(e)

View file

@ -146,6 +146,7 @@
android:layout_marginEnd="@dimen/standard_gap"
android:layout_marginRight="@dimen/standard_gap"
android:layout_marginBottom="@dimen/standard_gap"
android:hint="@string/_2fa_code"
android:visibility="gone"
app:passwordToggleEnabled="false"
tools:visibility="visible">
@ -154,9 +155,7 @@
android:id="@+id/login_two_factor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/_2fa_code"
android:imeOptions="flagNoExtractUi"
android:inputType="number"
android:visibility="gone"
tools:visibility="visible" />

View file

@ -102,6 +102,7 @@
android:layout_marginEnd="8dp"
android:text="SEND"
android:visibility="visible"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/textHeader"
app:layout_constraintHorizontal_bias="1.0"

View file

@ -0,0 +1,74 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/search_language"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:hint="Type Language Name"
android:padding="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/language_order_preference"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Language order preference"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search_language" />
<ListView
android:id="@+id/language_history_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@id/separator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/language_order_preference" />
<View
android:id="@+id/separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black"
android:layout_marginTop="10dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/language_history_list" />
<TextView
android:id="@+id/all_languages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="All Languages"
app:layout_constraintTop_toBottomOf="@id/separator"
app:layout_constraintEnd_toEndOf="@+id/language_history_list"
android:layout_margin="8dp"
app:layout_constraintStart_toStartOf="parent" />
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:id="@+id/language_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/all_languages" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -26,10 +26,11 @@
android:clickable="true"
android:focusable="true"
android:visibility="visible"
app:backgroundTint="@color/main_background_light"
app:backgroundTint="?attr/colorSurface"
app:elevation="@dimen/dimen_6"
app:fabSize="normal"
app:srcCompat="@drawable/ic_my_location_black_24dp"
app:tint="?attr/colorOnSurface"
app:useCompatPadding="true" />
<View

View file

@ -168,6 +168,7 @@
<string name="login_failed_throttled">الكثير من المحاولات غير الناجحة. الرجاء المحاولة مرة أخرى في بضع دقائق.</string>
<string name="login_failed_blocked">عذراً، لقد تم منع هذا المستخدم على كومنز</string>
<string name="login_failed_2fa_needed">يجب توفير رمز التحقق المزدوج.</string>
<string name="login_failed_email_auth_needed">تم إرسال رمز التحقق إلى بريدك الإلكتروني. يُرجى إدخال الرمز لتسجيل الدخول.</string>
<string name="login_failed_generic">فشل تسجيل الدخول</string>
<string name="share_upload_button">ارفع</string>
<string name="multiple_share_base_title">اسم هذه المجموعة</string>
@ -273,6 +274,7 @@
<string name="become_a_tester_title">انضم لمختبري اصدارات Beta (بيتا)</string>
<string name="become_a_tester_description">يمكنك الاشتراك في القناة التجريبية على جوجل بلاي والحصول على إمكانية الوصول المبكر إلى الميزات الجديدة وإصلاحات الأخطاء</string>
<string name="_2fa_code">رمز التحقق المزدوج 2FA</string>
<string name="email_auth_code">رمز التحقق من البريد الإلكتروني</string>
<string name="logout_verification">أترغب فعلا في الخروج؟</string>
<string name="mediaimage_failed">صورة الوسائط فشلت</string>
<string name="no_subcategory_found">لم يتم العثور على تصنيفات فرعية.</string>

View file

@ -119,6 +119,7 @@
<string name="login_failed_throttled">For mange mislykkede forsøg. Prøv igen om et par minutter.</string>
<string name="login_failed_blocked">Beklager, denne bruger er blevet blokeret på Commons</string>
<string name="login_failed_2fa_needed">Du skal angive din tofaktorgodkendelseskode.</string>
<string name="login_failed_email_auth_needed">En login-bekræftelseskode er blevet sendt til din e-mailadresse. Angiv koden for at logge ind.</string>
<string name="login_failed_generic">Login mislykkedes</string>
<string name="share_upload_button">Upload</string>
<string name="multiple_share_base_title">Navngiv dette sæt</string>
@ -224,6 +225,7 @@
<string name="become_a_tester_title">Bliv betatester</string>
<string name="become_a_tester_description">Registrer dig på vores betakanal på Google Play og få tidlig adgang til nye funktioner og fejlrettelser</string>
<string name="_2fa_code">2FA-kode</string>
<string name="email_auth_code">E-mail-bekræftelseskode</string>
<string name="logout_verification">Ønsker du at logge ud?</string>
<string name="mediaimage_failed">Mediebillede mislykkedes</string>
<string name="no_subcategory_found">Ingen underkategorier fundet</string>

View file

@ -315,7 +315,7 @@
<string name="share_image_via">Resımi vıla kerê pê</string>
<string name="no_achievements_yet" fuzzy="true">Hewna to iştirak nêkerdo</string>
<string name="account_created">Hesab vıraziya!</string>
<string name="text_copy">Metın kopyayê panoyi biyo</string>
<string name="text_copy">Metın be panoyi ra kopya bi</string>
<string name="notification_mark_read">Pêhesnayışi wanaye nışan bıkerê</string>
<string name="some_error">Yew xeta biye!</string>
<string name="place_state">Weziyetê cayi:</string>

View file

@ -22,6 +22,7 @@
* Fitoschido
* Gastonsaenz
* Hasley
* HylianAngel
* Ihojose
* JO777
* Jack30
@ -150,6 +151,7 @@
<string name="menu_from_camera">Tomar una foto</string>
<string name="menu_nearby">Cercanos</string>
<string name="provider_contributions">Mis subidas</string>
<string name="menu_copy_link">Copiar enlace</string>
<string name="menu_share">Compartir</string>
<string name="menu_view_file_page">Ver página del archivo</string>
<string name="share_title_hint">Leyenda (requerido)</string>
@ -413,7 +415,7 @@
<string name="statistics_thanks">Agradecimientos recibidos</string>
<string name="statistics_featured">Imágenes destacadas</string>
<string name="statistics_wikidata_edits">Imágenes vía \"Sitios Cercanos\"</string>
<string name="level" fuzzy="true">Nivel</string>
<string name="level">Nivel %d</string>
<string name="images_uploaded">Imágenes subidas</string>
<string name="image_reverts">Imágenes no revertidas</string>
<string name="images_used_by_wiki">Imágenes utilizadas</string>

View file

@ -853,8 +853,14 @@
<string name="usages_on_other_wikis_heading">Autres wikis</string>
<string name="bullet_point"></string>
<string name="file_usages_container_heading">Utilisations du fichier</string>
<string name="title_activity_single_web_view">SingleWebViewActivity</string>
<string name="account">Compte</string>
<string name="vanish_account">Faire disparaître le compte</string>
<string name="account_vanish_request_confirm_title">Avertissement de disparition du compte</string>
<string name="account_vanish_request_confirm">La disparition est un &lt;b&gt;dernier recours&lt;/b&gt; et ne devrait être &lt;b&gt;utilisée que quand vous voulez arrêter déditer pour toujours&lt;/b&gt; et aussi pour cacher autant que possible vos associations passées.&lt;br/&gt;&lt;br/&gt;La suppression de compte sur Wikimedia Commons se fait en changeant le nom de votre compte de sorte que les autres ne peuvent pas identifier vos contributions, lors dun processus appelé disparition du compte. &lt;b&gt;La disparition ne garantit pas lanonymat complet ni ne supprime les contributions de vos projets&lt;/b&gt;.</string>
<string name="caption">Légende</string>
<string name="caption_copied_to_clipboard">Légende copiée dans le presse-papier</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Félicitations, toutes les images dans cet album ont été soit téléchargées soit marquées comme non téléchargeables.</string>
<string name="show_in_explore">Afficher dans Explorer</string>
<string name="show_in_nearby">Afficher à proximité</string>
</resources>

View file

@ -40,13 +40,13 @@
<item quantity="one">(%1$d)</item>
<item quantity="other">(%1$d)</item>
</plurals>
<plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="one">Iniciando %1$d carga</item>
<item quantity="other">Iniciando %1$d cargas</item>
<plurals name="starting_multiple_uploads">
<item quantity="one">Procesando %d carga</item>
<item quantity="other">Procesando %d cargas</item>
</plurals>
<plurals name="multiple_uploads_title" fuzzy="true">
<item quantity="one">%1$d carga</item>
<item quantity="other">%1$d cargas</item>
<plurals name="multiple_uploads_title">
<item quantity="one">%d carga</item>
<item quantity="other">%d cargas</item>
</plurals>
<plurals name="share_license_summary">
<item quantity="one">Esta imaxe quedará baixo a licenza %1$s</item>
@ -80,16 +80,16 @@
<string name="updating_caption_title">A actualizar lendas e descricións</string>
<string name="updating_caption_message">Agarde un chisco…</string>
<string name="login_success">Accedeu correctamente!</string>
<string name="login_failed">Erro durante o inició de sesión!</string>
<string name="login_failed">Erro durante o inicio de sesión!</string>
<string name="upload_failed">Ficheiro non atopado. Por favor, probe con outro.</string>
<string name="retry_limit_reached">Alcanzouse o límite máximo de reintentos! Cancele o envío e ténteo de novo</string>
<string name="unrestricted_battery_mode">Desactivar a optimización da batería?</string>
<string name="authentication_failed">Fallou a autenticación. Inicie sesión de novo.</string>
<string name="uploading_started">A carga comezou!</string>
<string name="uploading_queued">Envío en cola (modo de conexión limitado activado)</string>
<string name="upload_completed_notification_title">Cargouse \"%1$s\"!</string>
<string name="upload_completed_notification_title">Subiuse \"%1$s\"!</string>
<string name="upload_completed_notification_text">Prema para ollar a súa carga</string>
<string name="upload_progress_notification_title_start">A enviar ficheiro: %s</string>
<string name="upload_progress_notification_title_start">A subir o ficheiro: %s</string>
<string name="upload_progress_notification_title_in_progress">Cargando \"%1$s\"</string>
<string name="upload_progress_notification_title_finishing">Rematando a carga de \"%1$s\"</string>
<string name="upload_failed_notification_title">Produciuse un erro ao enviar %1$s</string>
@ -352,7 +352,7 @@
<string name="statistics_thanks">Agradecementos recibidos</string>
<string name="statistics_featured">Imaxes destacadas</string>
<string name="statistics_wikidata_edits">Imaxes vía \"Lugares próximos\"</string>
<string name="level" fuzzy="true">Nivel</string>
<string name="level">Nivel %d</string>
<string name="images_uploaded">Imaxes cargadas</string>
<string name="image_reverts">Imaxes non revertidas</string>
<string name="images_used_by_wiki">Imaxes usadas</string>
@ -408,14 +408,14 @@
<string name="never_ask_again">Non volver a preguntar isto nunca</string>
<string name="display_location_permission_title">Solicitar permiso de localización</string>
<string name="display_location_permission_explanation">Pedir permisos de localización cando sexa necesario para a funcionalidade de notificación de proximidade.</string>
<string name="achievements_fetch_failed" fuzzy="true">Algo foi mal, non puidemos obter as túas achegas</string>
<string name="achievements_fetch_failed">Algo foi mal e non puidemos obter os logros</string>
<string name="ends_on">Finaliza o:</string>
<string name="display_campaigns">Amosar campañas</string>
<string name="display_campaigns_explanation">Ver as campañas en curso</string>
<string name="option_allow">Permitir</string>
<string name="option_dismiss">Descartar</string>
<string name="nearby_campaign_dismiss_message">Xa non verá as campañas. Porén, pode volver habilitar esta notificación na configuración.</string>
<string name="this_function_needs_network_connection" fuzzy="true">Esta función require conexión de rede, verifique a súa configuración de conexión.</string>
<string name="this_function_needs_network_connection">Esta función necesita conexión de rede. Verifique a súa configuración de conexión.</string>
<string name="error_processing_image">Houbo un erro ó procesar a imaxe. Por favor, ténteo de novoǃ</string>
<string name="getting_edit_token">Obter un identificador para editar</string>
<string name="check_category_adding_template">Engadir modelo para o control de categoría</string>
@ -462,8 +462,8 @@
<string name="delete_helper_show_deletion_title_failed">Fallou</string>
<string name="delete_helper_show_deletion_message_else">Non foi posíbel solicitar a eliminación.</string>
<string name="delete_helper_ask_spam_selfie">Un autorretrato que non se emprega en ningún artigo</string>
<string name="delete_helper_ask_spam_blurry" fuzzy="true">Borrosa</string>
<string name="delete_helper_ask_spam_nonsense" fuzzy="true">Sen sentido</string>
<string name="delete_helper_ask_spam_blurry">completamente borrosa</string>
<string name="delete_helper_ask_spam_nonsense">sen sentido, totalmente inusable en calquera artigo</string>
<string name="delete_helper_ask_reason_copyright_press_photo">Foto de prensa</string>
<string name="delete_helper_ask_reason_copyright_internet_photo">Foto aleatoria de internet</string>
<string name="delete_helper_ask_reason_copyright_logo">Logo</string>

View file

@ -385,8 +385,11 @@
<string name="notifications">Avizi</string>
<string name="read_notifications">Avizi (lektita)</string>
<string name="display_nearby_notification">Montrez proxima avizo</string>
<string name="display_nearby_notification_summary">Montrar avizo en utensilo \'\'app\'\' pri la maxim proxima loko qua bezonas pikturi</string>
<string name="list_sheet">Listo</string>
<string name="storage_permission">Permiso pri enmagazinigo</string>
<string name="write_storage_permission_rationale_for_image_share">Ni bezonas vua permiso por acesar l\'extera enmagazinigado di vua utensilo, por sendar imaji.</string>
<string name="nearby_notification_dismiss_message">Vu ne pluse vidos la maxim proxima loko qua bezonas fotografuro. Tamen, vu povas kapabligar itere ca avizo en Ajusti (\'\'Settings\'\'), se vu deziros.</string>
<string name="step_count">Etapo %1$d de %2$d: %3$s</string>
<string name="next">Sequanta</string>
<string name="previous">Antea</string>
@ -411,17 +414,32 @@
<string name="no_depictions_selected">Nula deskripturo selektita</string>
<string name="back_button_warning">Cesar kargajo</string>
<string name="back_button_continue">Durar kargajo</string>
<string name="upload_flow_all_images_in_set">(Por omna imaji en la grupo)</string>
<string name="search_this_area">Serchez ca areo</string>
<string name="nearby_card_permission_title">Permiso bezonata</string>
<string name="nearby_card_permission_explanation">Ka vu deziras ke ni uzez vua nuna lokizo por montrar vicina loki qui bezonas imaji?</string>
<string name="unable_to_display_nearest_place">Ne esas posibla montrar la maxim proxima loko qua bezonas imajo, se vu ne montros vua lokizo</string>
<string name="never_ask_again">Ne pluse demandez to</string>
<string name="display_location_permission_title">Demandar lokala permiso</string>
<string name="display_location_permission_explanation">Demandez lokala permiso, kande bezonata por uzar karto montranta proximeso.</string>
<string name="achievements_fetch_failed">Ulu faliis, ni ne povis montrar vua sucesi</string>
<string name="achievements_fetch_failed_ultimate_achievement">Vu facis plu multa kontributaji, e nia kalkulo-utensilo ne povis kalkular li. To esis la maxim importanta suceso.</string>
<string name="ends_on">Finas la:</string>
<string name="display_campaigns">Montrez kampanii</string>
<string name="display_campaigns_explanation">Videz la kampanii duranta</string>
<string name="in_app_camera_location_access_explanation">Permisez ke l\'utensilo (\'\'app\'\') lokizez, se la kamero ne enrejistros la lokizo. Kelka kameri ne havas utensilo por enrejistrar lokizo. Cakaze, vua kontributado divenos plu utila se vu permisos ke l\'\'\'app\'\' prenez ed enrejistrez lokizi. Vu povos abrogar ca permiso irgatempe en Ajusti (\'\'Settings\'\')</string>
<string name="option_allow">Permisar</string>
<string name="option_dismiss">Eskartar</string>
<string name="in_app_camera_needs_location">Voluntez kapabligar registrago di lokizo en \'\'Settings\'\', e probez itere.\n\nNoto: l\'arkivo sendanta povas ne havar informo pri lokizo, se l\'\'\'app\'\' ne povas rekuperar l\'informo pri lokizo en kurta intervalo.</string>
<string name="getting_edit_token">Kaptanta \'\'token\'\' por redaktar.</string>
<string name="check_category_adding_template">Adjuntanta shablono por verifikar kategorio</string>
<string name="check_category_notification_title">Demandanta verifiko di kategorio por %1$s</string>
<string name="check_category_edit_summary">Demandanta verifiko di kategorio</string>
<string name="check_category_success_title">Verifiko di kategorio demandita</string>
<string name="check_category_failure_title">Demando pri verifiko di kategorio ne funcionis</string>
<string name="check_category_success_message">Dema%1$sndita verifiko di kategorio por %1$s</string>
<string name="check_category_failure_message">Ne povis demandar verifiko di kategorio por %1$s</string>
<string name="check_category_toast">Demandanta verifiko di kategorio por %1$s</string>
<string name="nominate_for_deletion_done">Facita</string>
<string name="send_thank_success_title">Sendanta danko: Suceso</string>
<string name="send_thank_success_message">Danko sendita sucese a %1$s</string>
@ -430,6 +448,7 @@
<string name="send_thank_toast">Sendanta danko a %1$s</string>
<string name="review_copyright">Ka to obedias la reguli pri autoroyuro?</string>
<string name="review_category">Ka lua kategorio esas korekta?</string>
<string name="review_spam">Ka to apartenas al skopo dil projeto?</string>
<string name="review_thanks">Ka vu deziras dankar la kontributero?</string>
<string name="review_spam_explanation">Kliktez NO por indikar ca imajo por efaco, se ol ne havas irga utileso.</string>
<string name="review_no_category">Ho, to ne mem havas kategorio!</string>
@ -452,9 +471,11 @@
<string name="please_wait">Vartez...</string>
<string name="copied_successfully">Kopiita</string>
<string name="welcome_do_upload_content_description">Exempli pri bona imaji por sendar a Commons</string>
<string name="welcome_dont_upload_content_description">Exempli pri imaji por NE SENDAR</string>
<string name="skip_image">Saltez ca imajo</string>
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Descharjo faliis!! Ni ne povis descharjar l\'arkivo sen permiso pri extera konservo.</string>
<string name="manage_exif_tags">Administrar etiketi EXIF</string>
<string name="manage_exif_tags_summary">Dum sendo di arkivi, selektez quala etiketi EXIF devas mantenesar</string>
<string name="exif_tag_name_author">Autoro</string>
<string name="exif_tag_name_copyright">Autoroyuro</string>
<string name="exif_tag_name_location">Loko</string>
@ -465,10 +486,14 @@
<string name="image_info">Informo pri imajo</string>
<string name="no_categories_found">Nula kategorio trovesis</string>
<string name="default_description_language">implicita deskripto-linguo</string>
<string name="delete_helper_show_deletion_title">Indikita por efaco</string>
<string name="delete_helper_show_deletion_title_success">Suceso</string>
<string name="delete_helper_show_deletion_message_if">Indikita %1$s por efaco.</string>
<string name="delete_helper_show_deletion_title_failed">Faliis</string>
<string name="delete_helper_show_deletion_message_else">Ne povis demandar efaco.</string>
<string name="delete_helper_ask_spam_selfie">\'\'selfie\'\'-imajo qua ne uzesas en irga artiklo</string>
<string name="delete_helper_ask_spam_blurry">komplete neklara</string>
<string name="delete_helper_ask_spam_nonsense">sensencajo, qua ne povas uzesar en irga artiklo</string>
<string name="delete_helper_ask_reason_copyright_press_photo">Fotografuro de komunikilaro</string>
<string name="delete_helper_ask_reason_copyright_internet_photo">Hazarda imajo de Interreto</string>
<string name="delete_helper_ask_reason_copyright_logo">Emblemo</string>
@ -534,9 +559,13 @@
<string name="theme_dark_name">Koloro obskura</string>
<string name="theme_light_name">Koloro klara</string>
<string name="load_more">Charjez pluse</string>
<string name="add_picture_to_wikipedia_article_title">Adjuntar imajo a Wikipedio</string>
<string name="add_picture_to_wikipedia_article_desc">Ka vu deziras adjuntar ca imajo al artiklo de Wikipedio en idiomo %1$s?</string>
<string name="confirm">Konfirmez</string>
<string name="instructions_title">Instrucioni</string>
<string name="wikipedia_instructions_step_1">1. Uzez la sequanta wikitexto:</string>
<string name="wikipedia_instructions_step_2">Kliktanta \"konfirmar\" (\'\'Confirm\'\') apertos l\'artiklo che Wikipedio</string>
<string name="wikipedia_instructions_step_3">3. Trovez adequata fako dil artiklo por inkluzar vua imajo</string>
<string name="pause">pauzar</string>
<string name="resume">durigar</string>
<string name="paused">Pauzita</string>

View file

@ -144,6 +144,7 @@
<string name="login_failed_throttled">יותר מדי ניסיונות כניסה כושלים. נא לנסות שוב בעוד כמה דקות.</string>
<string name="login_failed_blocked">סליחה, החשבון הזה חסום בוויקישיתוף</string>
<string name="login_failed_2fa_needed">יש לספק את קוד האימות הדו־שלבי שלך.</string>
<string name="login_failed_email_auth_needed">נשלח קוד אימות כניסה לכתובת הדוא״ל שלך. נא לספק את הקוד כדי להיכנס.</string>
<string name="login_failed_generic">הכניסה נכשלה</string>
<string name="share_upload_button">העלאה</string>
<string name="multiple_share_base_title">שם האוסף</string>
@ -249,6 +250,7 @@
<string name="become_a_tester_title">להפוך לבודק בטא</string>
<string name="become_a_tester_description">להירשם לערוץ הבטא שלנו בחנות גוגל Play ולקבל גישה מוקדמת לאפשרויות חדשות ותיקוני באגים</string>
<string name="_2fa_code">קוד אימות דו־שלבי</string>
<string name="email_auth_code">קוד אימות בדוא״ל</string>
<string name="logout_verification">האם באמת לצאת מהחשבון?</string>
<string name="mediaimage_failed">תמונת המדיה נכשלה</string>
<string name="no_subcategory_found">לא נמצאו תת־קטגוריות</string>

View file

@ -119,7 +119,7 @@
<string name="login_failed_network">ログインできません - ネットワークのエラーです</string>
<string name="login_failed_throttled">失敗した回数が多すぎます。数分後にもう一度お試しください。</string>
<string name="login_failed_blocked">申し訳ありませんが、この利用者はコモンズでブロックされています。</string>
<string name="login_failed_2fa_needed">2段階認証コードを入力してください。</string>
<string name="login_failed_2fa_needed">二要素認証コードを入力してください。</string>
<string name="login_failed_generic">ログイン失敗</string>
<string name="share_upload_button">アップロード</string>
<string name="multiple_share_base_title">このセットに名前をつけてください</string>
@ -221,7 +221,7 @@
<string name="media_detail_coordinates_empty">情報なし</string>
<string name="become_a_tester_title">ベータ版テスターになる</string>
<string name="become_a_tester_description">Google Playのベータ版チャンネルにオプトインして、新機能やバグ修正プログラムに早期にアクセス</string>
<string name="_2fa_code">2段階認証コード</string>
<string name="_2fa_code">2FAコード</string>
<string name="logout_verification">ログアウトしてもよろしいですか?</string>
<string name="mediaimage_failed">メディアイメージが失敗しました</string>
<string name="no_subcategory_found">下位カテゴリは見つかりませんでした</string>

View file

@ -101,6 +101,8 @@
<string name="menu_from_camera">Суратха ал</string>
<string name="menu_nearby">Джуўукъда</string>
<string name="provider_contributions">Джюклегенлерим</string>
<string name="menu_copy_link">Джибериуню копия эт</string>
<string name="menu_link_copied">Джибериу алмашдырыу буферге копия этилгенди</string>
<string name="menu_share">Юлюшле</string>
<string name="menu_view_file_page">Файлны бетине къара</string>
<string name="share_title_hint">Тюб джазыу (Амалсыз)</string>
@ -274,6 +276,7 @@
<string name="copy_wikicode">Викитекстни алмашдырыу буферге копия эт</string>
<string name="wikicode_copied">Викитекст алмашдырыу буферге копия этилди</string>
<string name="nearby_location_not_available">Джууукъдагыла тюз ишлеялмайды, Локация хайырландырылалмайды.</string>
<string name="nearby_showing_pins_offline">Интернет джетишмейди. Къуру кэш этилген джерле кёргюзюледиле.</string>
<string name="upload_location_access_denied">Локациягъа джетишиу уналмады. Бу функцияны хайырланыр ючюн, тилейбиз, локациягъызны къолугъуз бла белгилегиз.</string>
<string name="location_permission_rationale_nearby">Джууукъдагъы джерле тизмени кёргюзюр ючююн, эркинлик берирге керекди</string>
<string name="location_permission_rationale_explore">Джууукъдагъы суратла тизмени кёргюзюр ючююн, эркинлик берирге керекди</string>
@ -357,11 +360,13 @@
<string name="delete">Кетер</string>
<string name="Achievements">Джетишимле</string>
<string name="Profile">Профиль</string>
<string name="badges">Белгичикле</string>
<string name="statistics">Статистика</string>
<string name="statistics_thanks">Бюсюреуле Алындыла</string>
<string name="statistics_featured">Сайланнган Суратла</string>
<string name="statistics_wikidata_edits">\"Джууукъдагъы Джерле\" юсю бла суратла</string>
<string name="level" fuzzy="true">Дараджа</string>
<string name="level">Дараджа %d</string>
<string name="profileLevel">%s (Дараджа %s)</string>
<string name="images_uploaded">Суратла Джюклендиле</string>
<string name="image_reverts">Суратла Кери Алынмадыла</string>
<string name="images_used_by_wiki">Суратла Хайырландыла</string>
@ -393,6 +398,7 @@
<string name="map_application_missing">Девайсыгъызда келишген картография къошакъ табылмады. Тилейбиз, бу энчиликни хайырландырыр ючюн картография къошакъ джюклегиз.</string>
<string name="title_page_bookmarks_pictures">Суратла</string>
<string name="title_page_bookmarks_locations">Локацияла</string>
<string name="title_page_bookmarks_categories">Категорияла</string>
<string name="menu_bookmark">Китаб белгилени къош/къорат</string>
<string name="provider_bookmarks">Китаб белгиле</string>
<string name="bookmark_empty">Алкъын чырт китаб белги къошмадыгъыз</string>
@ -794,4 +800,19 @@
<string name="red_pin">Бу джерни сураты джокъду, хайда бирин эт!</string>
<string name="green_pin">Бу джерни алайсыз да сураты барды.</string>
<string name="grey_pin">Бу джерни сураты болуб-болмагъанын тинте турама.</string>
<string name="error_while_loading">Джюкленнген заманда халат</string>
<string name="no_usages_found">Бир хайырланыучу да табылмады</string>
<string name="usages_on_commons_heading">Гёзен</string>
<string name="usages_on_other_wikis_heading">Башха викиле</string>
<string name="file_usages_container_heading">Файлны хайырланыулары</string>
<string name="title_activity_single_web_view">SingleWebViewActivity</string>
<string name="account">Хыйсаб</string>
<string name="vanish_account">Хыйсабны сюрт</string>
<string name="account_vanish_request_confirm_title">Хыйсаб сюртюуню эсгертиую</string>
<string name="account_vanish_request_confirm">Джокъ этиу — &lt;b&gt;ахыр амалды&lt;/b&gt;, эмда аны &lt;b&gt;тюзетиуню тамамы бла тохтатыргъа излегесиз хайырланыргъа керекди&lt;/b&gt;, неда эскиде ассоциацияланы бир мадар болуб аслам джашырыргъа излесегиз.&lt;br/&gt;&lt;br /&gt;Викигёзенде хыйсабны кетериу, башхала хыйсабны джокъ этиу атны джюрютген процессде сизни кошумугъузну танымазча, хыйсабыгъызны атын тюрлендириу бла этиледи.&lt;b&gt;Джокъ этиу толу анонимликни гарантия этмейди эмда проектде къошумларыгъызны къоратмайды&lt;/b&gt;.</string>
<string name="caption">Тюб джазыу</string>
<string name="caption_copied_to_clipboard">Тюб джазыу алмашдырыу буферге копия этилгенди</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Алгъышлайбыз, бу альбомна бютеу сратла не джюкленнгендиле, неда джюкленирге джораланмагъанлача белгиленнгендиле.</string>
<string name="show_in_explore">Explore-де кёргюз</string>
<string name="show_in_nearby">Nearby-да кёргюз</string>
</resources>

View file

@ -179,7 +179,7 @@
<string name="statistics_thanks">Spasî Hate Wergirtin</string>
<string name="statistics_featured">Wêneyên Bijartî</string>
<string name="statistics_wikidata_edits">Wêneyên bi riya \"Cihên Nêz\"</string>
<string name="level" fuzzy="true">Derece</string>
<string name="level">Derece %d</string>
<string name="images_uploaded">Wêneyên Barkirî</string>
<string name="review_thanks_yes_button_text">Wêneyê din</string>
<string name="review_thanks_no_button_text">Belê, çima na</string>

View file

@ -70,7 +70,7 @@
<string name="authentication_failed">D\'Authentifizéierung huet net funktionéiert. Loggt Iech wgl. nach eng Kéier an.</string>
<string name="uploading_started">D\'Eroplueden huet ugefaang!</string>
<string name="upload_completed_notification_title">%1$s eropgelueden!</string>
<string name="upload_completed_notification_text">Dréckt fir de Fichier ze gesinn deen Dir eropgelueden hutt</string>
<string name="upload_completed_notification_text">Tippt fir de Fichier ze gesinn, deen Dir eropgelueden hutt</string>
<string name="upload_progress_notification_title_start">Fichier eroplueden: %s</string>
<string name="upload_progress_notification_title_in_progress">%1$s gëtt eropgelueden</string>
<string name="upload_progress_notification_title_finishing">Eropluede vu(n) %1$s ofschléissen</string>
@ -99,6 +99,7 @@
<string name="login_failed_throttled">Ze dacks ouni Succès probéiert. Probéiert wgl. an e puer Minutten nach eng Kéier.</string>
<string name="login_failed_blocked">Pardon, dëse Benotzer ass op Commons gespaart</string>
<string name="login_failed_2fa_needed">Dir musst de Code vun Ärer Zwee-Facteur-Authentifizéierung uginn.</string>
<string name="login_failed_email_auth_needed">Dir hutt ee Login-Verifikatiounscode per E-Mail geschéckt kritt. Gitt wgl. de Code an, fir Iech anzeloggen.</string>
<string name="login_failed_generic">Aloggen huet net funktionéiert</string>
<string name="share_upload_button">Eroplueden</string>
<string name="multiple_share_base_title">Gitt dëser Biller een Numm</string>
@ -192,6 +193,7 @@
<string name="become_a_tester_title">Beta-Tester ginn</string>
<string name="become_a_tester_description">Schreift Iech op GooglePlay a fir eise Beta-Kanal a kritt fréi Zougang zu neie Funktiounen a Verbesserunge vu Feeler</string>
<string name="_2fa_code">2FA-Code</string>
<string name="email_auth_code">E-Mail-Verifikatiounscode</string>
<string name="logout_verification">Wëllt dir Iech wierklech ausloggen?</string>
<string name="no_subcategory_found">Keng Ënnerkategorie fonnt</string>
<string name="welcome_image_mount_zao">Bierg Zao</string>
@ -274,6 +276,7 @@
<string name="menu_search_button">Sichen</string>
<string name="search_commons">Op Commons sichen</string>
<string name="title_activity_search">Sichen</string>
<string name="search_recent_header">Rezent gesicht:</string>
<string name="search_tab_title_media">Medien</string>
<string name="search_tab_title_categories">Kategorien</string>
<string name="search_tab_title_depictions">Elementer</string>

View file

@ -113,6 +113,7 @@
<string name="login_failed_throttled">Направени се премногу неуспешни обиди. Обидете се пак за некоја минута.</string>
<string name="login_failed_blocked">За жал, корисникот е блокиран на Ризницата</string>
<string name="login_failed_2fa_needed">Мора да го укажете вашиот код за двочинителска заверка.</string>
<string name="login_failed_email_auth_needed">На вашата е-поштенска адреса ви испративме потврден код за најава. Внесете го кодот за да се најавите.</string>
<string name="login_failed_generic">Најавата не успеа</string>
<string name="share_upload_button">Подигни</string>
<string name="multiple_share_base_title">Дајте му име на овој комплет</string>
@ -218,6 +219,7 @@
<string name="become_a_tester_title">Станете бета-испробувач</string>
<string name="become_a_tester_description">Пријавете се на нашиот бета-канал на Google Play и добивајте ран пристап до нови можности и исправки на грешки</string>
<string name="_2fa_code">З-код</string>
<string name="email_auth_code">Испрати го потврдниот код на е-пошта</string>
<string name="logout_verification">Дали навистина сакате да се одјавите?</string>
<string name="mediaimage_failed">Сликата не успеа</string>
<string name="no_subcategory_found">Не пронајдов поткатегории</string>

View file

@ -22,7 +22,7 @@
<string name="nearby_row_image">ਤਸਵੀਰ</string>
<string name="nearby_all">ਸਾਰੇ</string>
<string name="appwidget_img">ਦਿਨ ਦੀ ਤਸਵੀਰ</string>
<plurals name="uploads_pending_notification_indicator" fuzzy="true">
<plurals name="uploads_pending_notification_indicator">
<item quantity="one"> ਫ਼ਾਈਲ ਚੜ੍ਹਾਈ ਜਾ ਰਹੀ ਹੈ</item>
<item quantity="other">%1$d ਫ਼ਾਈਲਾਂ ਚੜ੍ਹਾਈਆਂ ਜਾ ਰਹੀਆਂ ਹਨ</item>
</plurals>
@ -45,7 +45,7 @@
<string name="preference_category_general">ਆਮ</string>
<string name="preference_category_feedback">ਸੁਝਾਅ</string>
<string name="preference_category_privacy">ਪਰਦੇਦਾਰੀ</string>
<string name="app_name" fuzzy="true">ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼</string>
<string name="app_name">ਵਿਕੀਮੀਡੀਆ ਸ਼ਾਮਲਾਟ</string>
<string name="menu_settings">ਪਸੰਦਾਂ</string>
<string name="upload_in_progress">ਚੜ੍ਹਾਉਣਾ ਜਾਰੀ ਐ</string>
<string name="username">ਵਰਤੋਂਕਾਰ ਨਾਂ</string>
@ -180,8 +180,11 @@
<string name="media_detail_coordinates_empty">ਕੋਈ ਉਪਲਬਧ ਨਹੀਂ</string>
<string name="_2fa_code">2FA ਕੋਡ</string>
<string name="logout_verification">ਕੀ ਤੁਸੀਂ ਸੱਚੀਂ ਬੰਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?</string>
<string name="welcome_image_llamas">ਲਾਮਾ</string>
<string name="welcome_image_rainbow_bridge">ਸਤਰੰਗੀ ਪੁਲ</string>
<string name="welcome_image_welcome_wikipedia">ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਜੀ ਆਇਆਂ ਨੂੰ</string>
<string name="welcome_image_welcome_copyright">ਜੀ ਆਇਆਂ ਨੂੰ ਕਾਪੀਰਾਈਟ</string>
<string name="welcome_image_sydney_opera_house">ਸਿਡਨੀ ਓਪੇਰਾ ਹਾਊਸ</string>
<string name="cancel">ਰੱਦ ਕਰੋ</string>
<string name="navigation_drawer_open">ਖੋਲ੍ਹੋ</string>
<string name="navigation_drawer_close">ਬੰਦ ਕਰੋ</string>
@ -191,10 +194,13 @@
<string name="navigation_item_about">ਬਾਰੇ</string>
<string name="navigation_item_settings">ਤਰਜੀਹਾਂ</string>
<string name="navigation_item_feedback">ਸੁਝਾਅ</string>
<string name="navigation_item_feedback_github">ਗਿੱਟਹਬ (GitHub) ਰਾਹੀਂ ਸੁਝਾਅ</string>
<string name="navigation_item_logout">ਬਾਹਰ ਆਉ</string>
<string name="navigation_item_info">ਸਿਖਲਾਈ</string>
<string name="navigation_item_notification">ਸੂਚਨਾਵਾਂ</string>
<string name="navigation_item_review">ਪਰਖੋ</string>
<string name="no_description_found">ਕੋਈ ਵੇਰਵਾ ਨਹੀਂ ਮਿਲਿਆ</string>
<string name="nearby_info_menu_wikidata_article">ਵਿਕੀਡਾਟਾ ਵਸਤਾਂ</string>
<string name="nearby_info_menu_wikipedia_article">ਵਿਕੀਪੀਡੀਆ ਲੇਖ</string>
<string name="upload_problem_image_dark">ਤਸਵੀਰ ਬਹੁਤ ਗੂੜ੍ਹੀ ਹੈ।</string>
<string name="upload_problem_image_blurry">ਤਸਵੀਰ ਧੁੰਦਲੀ ਹੈ।</string>
@ -203,35 +209,76 @@
<string name="navigation_item_login">ਦਾਖ਼ਲ ਹੋਵੋ</string>
<string name="nearby_wikidata">ਵਿਕੀਡੇਟਾ</string>
<string name="nearby_wikipedia">ਵਿਕੀਪੀਡੀਆ</string>
<string name="about_rate_us">ਸਾਨੂੰ ਦਰਜਾ ਦਿਓ</string>
<string name="about_faq">ਅਕਸਰ ਪੁੱਛੇ ਜਾਂਦੇ ਸੁਆਲ</string>
<string name="user_guide">ਵਰਤੋਂਕਾਰ ਦਸਤਿਆਂ</string>
<string name="welcome_skip_button">ਸਿਖਲਾਈ ਛੱਡੋ</string>
<string name="about_translate">ਤਰਜਮਾ ਕਰੋ</string>
<string name="about_translate_title">ਬੋਲੀਆਂ</string>
<string name="about_translate_proceed">ਅੱਗੇ ਵਧੋ</string>
<string name="about_translate_cancel">ਰੱਦ ਕਰੋ</string>
<string name="retry">ਮੁੜ-ਕੋਸ਼ਿਸ਼ ਕਰੋ</string>
<string name="showcase_view_needs_photo">ਇਸ ਜਗ੍ਹਾ ਨੂੰ ਇੱਕ ਤਸਵੀਰ ਦੀ ਲੋੜ ਏ।</string>
<string name="showcase_view_has_photo">ਇਸ ਜਗ੍ਹਾ \'ਤੇ ਪਹਿਲਾਂ ਹੀ ਇੱਕ ਤਸਵੀਰ ਏ।</string>
<string name="showcase_view_no_longer_exists">ਇਹ ਜਗ੍ਹਾ ਹੁਣ ਮੌਜੂਦ ਨਹੀਂ ਏ।</string>
<string name="no_images_found">ਕੋਈ ਤਸਵੀਰ ਨਹੀਂ ਲੱਭੀ!</string>
<string name="error_loading_images">ਤਸਵੀਰ ਚੜਾਉਨ ਵੇਲੇ ਗਲਤੀ ਆਈ ਏ।</string>
<string name="image_uploaded_by">%1$s: ਵੱਲੋਂ ਚੜ੍ਹਾਈ ਗਈ</string>
<string name="block_notification_title">ਰੋਕ ਲਾਈ ਗਈ</string>
<string name="app_widget_heading">ਦਿਨ ਦੀ ਤਸਵੀਰ</string>
<string name="menu_search_button">ਲੱਭੋ</string>
<string name="title_activity_search">ਲੱਭੋ</string>
<string name="search_recent_header">ਹਾਲੀਆ ਖੋਜਾਂ:</string>
<string name="provider_searches">ਹਾਲ ਦੀਆਂ ਪੁੱਛਗਿੱਛ ਖੋਜਾਂ</string>
<string name="provider_recent_languages">ਹਾਲ ਹੀ ਵਿੱਚ ਬੋਲੀਆਂ ਬਾਰੇ ਪੁੱਛਗਿੱਛ</string>
<string name="search_tab_title_categories">ਸ਼੍ਰੇਣੀਆਂ</string>
<string name="search_tab_title_depictions">ਵਸਤਾਂ</string>
<string name="explore_tab_title_map">ਨਕਸ਼ਾ</string>
<string name="question">ਸਵਾਲ</string>
<string name="result">ਨਤੀਜਾ</string>
<string name="user_not_logged_in">ਤੁਹਾਡੇ ਦਾਖਲੇ ਦੀ ਮਿਆਦ ਪੁੱਗ ਗਈ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਮੁੜ ਦਾਖਲ ਹੋਵੋ।</string>
<string name="continue_message">ਜਾਰੀ ਰੱਖੋ</string>
<string name="correct">ਸਹੀ ਜਵਾਬ</string>
<string name="wrong">ਗਲਤ ਜਵਾਬ</string>
<string name="share_app_title">ਐਪ ਸਾਂਝਾ ਕਰੋ</string>
<string name="rotate">ਘੁੰਮਾਓ</string>
<string name="error_fetching_nearby_places">ਨੇੜਲੀਆਂ ਥਾਵਾਂ ਲੋਡ ਨਹੀਂ ਕੀਤੀਆਂ ਜਾ ਸਕੀਆਂ</string>
<string name="no_pictures_in_this_area">ਇਸ ਖੇਤਰ ਵਿੱਚ ਕੋਈ ਤਸਵੀਰਾਂ ਨਹੀਂ ਹਨ।</string>
<string name="no_nearby_places_around">ਆਲੇ-ਦੁਆਲੇ ਕੋਈ ਨੇੜਲੀ ਥਾਂ ਨਹੀਂ ਏ।</string>
<string name="error_fetching_nearby_monuments">ਨੇਡ਼ਲੇ ਸਮਾਰਕਾਂ ਨੂੰ ਲਿਆਉਣ ਵਿੱਚ ਗਲਤੀ।</string>
<string name="no_recent_searches">ਕੋਈ ਤਾਜ਼ਾ ਖੋਜ ਨਹੀਂ</string>
<string name="delete_recent_searches_dialog">ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ ਉੱਤੇ ਆਪਣੇ ਖੋਜ ਇਤਿਹਾਸ ਨੂੰ ਸਾਫ਼ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?</string>
<string name="delete_search_dialog">ਕੀ ਤੁਸੀਂ ਇਸ ਖੋਜ ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?</string>
<string name="search_history_deleted">ਖੋਜ ਇਤਿਹਾਸ ਮਿਟਾਇਆ ਗਿਆ</string>
<string name="nominate_delete">ਮਿਟਾਉਣ ਲਈ ਨਾਮਜ਼ਦ ਕਰੋ</string>
<string name="delete">ਮਿਟਾਓ</string>
<string name="Achievements">ਪ੍ਰਾਪਤੀਆਂ</string>
<string name="Profile">ਪ੍ਰੋਫਾਈਲ</string>
<string name="statistics">ਅੰਕੜੇ</string>
<string name="statistics_thanks">ਧੰਨਵਾਦ ਪ੍ਰਾਪਤ ਹੋਏ</string>
<string name="statistics_featured">ਵਿਸ਼ੇਸ਼ ਤਸਵੀਰ</string>
<string name="statistics_wikidata_edits">\"ਨੇੜਲੀਆਂ ਥਾਵਾਂ\" ਰਾਹੀਂ ਤਸਵੀਰਾਂ</string>
<string name="level">ਪੱਧਰ %d</string>
<string name="images_uploaded">ਤਸਵੀਰਾਂ ਚੜ੍ਹਾਇਆਂ ਗਈਆਂ</string>
<string name="image_reverts">ਤਸਵੀਰਾਂ ਵਾਪਸ ਨਹੀਂ ਕੀਤੀ ਗਈਆਂ</string>
<string name="images_used_by_wiki">ਵਰਤੀ ਗਈਆਂ ਤਸਵੀਰਾਂ</string>
<string name="achievements_share_message">ਆਪਣੀਆਂ ਪ੍ਰਾਪਤੀਆਂ ਨੂੰ ਆਪਣੇ ਦੋਸਤਾਂ ਨਾਲ ਸਾਂਝਾ ਕਰੋ!</string>
<string name="achievements_revert_limit_message">ਘੱਟੋ-ਘੱਟ ਲੋੜੀਂਦਾ:</string>
<string name="error_occurred">ਗਲਤੀ ਆਈ!</string>
<string name="contributions_fragment">ਯੋਗਦਾਨ</string>
<string name="nearby_fragment">ਨੇੜੇ-ਤੇੜੇ</string>
<string name="notifications">ਸੂਚਨਾਵਾਂ</string>
<string name="read_notifications">ਸੂਚਨਾਵਾਂ (ਪੜ੍ਹਿਆਂ)</string>
<string name="list_sheet">ਸੂਚੀ</string>
<string name="next">ਅੱਗੇ</string>
<string name="previous">ਪਿਛਲਾ</string>
<string name="title_page_bookmarks_pictures">ਤਸਵੀਰਾਂ</string>
<string name="title_page_bookmarks_locations">ਟਿਕਾਣਾ</string>
<string name="title_page_bookmarks_categories">ਸ਼੍ਰੇਣੀਆਂ</string>
<string name="search_this_area">ਇਸ ਖੇਤਰ ਵਿੱਚ ਖੋਜ ਕਰੋ</string>
<string name="nearby_card_permission_title">ਇਜਾਜ਼ਤ ਦੀ ਬੇਨਤੀ</string>
<string name="never_ask_again">ਇਹ ਮੁੜ ਕਦੇ ਨਾ ਪੁੱਛੋ</string>
<string name="display_location_permission_title">ਟਿਕਾਣੇਂ ਦੀ ਆਗਿਆ ਮੰਗੋ</string>
<string name="ends_on">ਨੂੰ ਮਿਆਦ ਪੁਗਦੀ</string>
<string name="display_campaigns">ਮੁਹਿੰਮਾਂ ਵੇਖਾਓ</string>
<string name="option_allow">ਇਜਾਜ਼ਤ ਦਿਓ</string>

View file

@ -111,6 +111,7 @@
<string name="login_failed_throttled">Tròpi tentativ falì. Për piasì, ch\'a preuva torna da-sì chèiche minute.</string>
<string name="login_failed_blocked">An dëspias, s\'utent-sì a l\'é stàit blocà ansima a Commons</string>
<string name="login_failed_2fa_needed">A dev fornì sò còdes d\'autentificassion a doi fator.</string>
<string name="login_failed_email_auth_needed">Un còdes ëd verìfica ëd conession a l\'é stàit mandà a soa adrëssa ëd pòsta eletrònica. Për piasì, ch\'a anserissa col còdes për intré ant ël sistema.</string>
<string name="login_failed_generic">Falì a rintré ant ël sistema</string>
<string name="share_upload_button">Carié</string>
<string name="multiple_share_base_title">Deje un nòm a s\'ansem</string>
@ -216,6 +217,7 @@
<string name="become_a_tester_title">Dventé në sperimentador Beta</string>
<string name="become_a_tester_description">Anscriv-se a nòstr canal beta su Google Play a oten-e n\'acess antissipà a le neuve fonsionalità e coression ëd givo</string>
<string name="_2fa_code">Còdes 2FA</string>
<string name="email_auth_code">Mandé un còdes ëd verifica</string>
<string name="logout_verification">Veul-lo për da bon seurte dal sistema?</string>
<string name="mediaimage_failed">Faliment ëd la plancia dël mojen</string>
<string name="no_subcategory_found">Gnun-e sot-categorìe trovà</string>

View file

@ -47,7 +47,14 @@
<item quantity="one">%d upload</item>
<item quantity="other">%d پورته کول</item>
</plurals>
<string name="share_license_summary" fuzzy="true">دا انځور به د %1$s په منښتليک سمبال وي.</string>
<plurals name="share_license_summary">
<item quantity="one">دا انځور به د منښتليک %1$s لاندې وي</item>
<item quantity="other">دا انځورونه به د %1$s منښتليک لاندې وي</item>
</plurals>
<plurals name="upload_count_title">
<item quantity="one">%1$d راپورته کول</item>
<item quantity="other">%1$d راپورته کېدنې</item>
</plurals>
<string name="navigation_item_explore">سپړنه</string>
<string name="preference_category_appearance">ښکارېدنه</string>
<string name="preference_category_general">ټولګړی</string>

View file

@ -419,7 +419,7 @@
<string name="provider_bookmarks">Favoritos</string>
<string name="bookmark_empty">Não adicionou nenhum favorito</string>
<string name="provider_bookmarks_location">Favoritos</string>
<string name="log_collection_started">A recolha de registos foi iniciada. REINICIE a aplicação, execute a operação que pretende registar e prima outra vez \"Enviar ficheiro de registos\"</string>
<string name="log_collection_started">A coleta de registros foi iniciada. REINICIE o aplicativo, execute a operação que pretende registrar e toque em Enviar arquivo de registros novamente</string>
<string name="deletion_reason_uploaded_by_mistake">Eu fiz o carregamento por engano</string>
<string name="deletion_reason_publicly_visible">Eu não sabia que seria publicamente visível</string>
<string name="deletion_reason_bad_for_my_privacy">Eu percebi que é ruim para minha privacidade</string>
@ -696,7 +696,7 @@
<string name="place_state_wlm">WLM</string>
<string name="wlm_upload_info">Essa imagem será enviada ao concurso Wiki Loves Monuments</string>
<string name="display_monuments">Monumentos de exibição</string>
<string name="wlm_month_message">Estamos no mês no Wiki Loves Monuments!</string>
<string name="wlm_month_message">Chegou o mês do Wiki Loves Monuments!</string>
<string name="learn_more">SABER MAIS</string>
<string name="wlm_campaign_title">Wiki Loves Monuments</string>
<string name="wlm_campaign_description">O Wiki Loves Monuments é um concurso internacional organizado pela Wikimedia sobre fotografias de monumentos</string>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* AguzulH
* Brahim-essaidi
* Hakim1bal
-->
@ -79,7 +80,7 @@
<string name="Profile">ⵉⴼⵔⵙ</string>
<string name="statistics">ⵉⵙⵉⴹⵏⵏ</string>
<string name="title_app_shortcut_setting">ⵜⵉⵙⵖⴰⵍ</string>
<string name="leaderboard_column_user">ⴰⵏⵙⵎⵔⵙ</string>
<string name="leaderboard_column_user">ⴰⵏⵙⵎⵔ</string>
<string name="leaderboard_column_count">ⵙⵙⵉⴹⵏ</string>
<string name="leaderboard_yearly">ⴰⵙⴳⵯⵙⴰⵏ</string>
<string name="leaderboard_weekly">ⴰⵏⵎⵍⴰⵙⵙ</string>

View file

@ -136,6 +136,7 @@
<string name="login_failed_throttled">失敗次數過多。請於幾分鐘後重試。</string>
<string name="login_failed_blocked">很抱歉,該使用者已被維基共享資源封禁</string>
<string name="login_failed_2fa_needed">必須提供您的雙重驗證代碼。</string>
<string name="login_failed_email_auth_needed">登入驗證碼已發送到您的電子郵件地址。請提供驗證碼以登入。</string>
<string name="login_failed_generic">登入失敗</string>
<string name="share_upload_button">上傳</string>
<string name="multiple_share_base_title">給這個集合命名</string>
@ -241,6 +242,7 @@
<string name="become_a_tester_title">成為 Beta 測試員</string>
<string name="become_a_tester_description">選擇加入我們在 Google Play 上的測試版頻道並儘早訪問新功能和錯誤修復</string>
<string name="_2fa_code">2FA 代碼</string>
<string name="email_auth_code">電子郵件驗證碼</string>
<string name="logout_verification">您確定要登出嗎?</string>
<string name="mediaimage_failed">媒體圖片失敗</string>
<string name="no_subcategory_found">找不到子分類</string>

View file

@ -111,6 +111,7 @@
<string name="login_failed_throttled">Too many unsuccessful attempts. Please try again in a few minutes.</string>
<string name="login_failed_blocked">Sorry, this user has been blocked on Commons</string>
<string name="login_failed_2fa_needed">You must provide your two factor authentication code.</string>
<string name="login_failed_email_auth_needed">A login verification code has been sent to your email address. Please provide the code to log in.</string>
<string name="login_failed_generic">Log-in failed</string>
<string name="share_upload_button">Upload</string>
<string name="multiple_share_base_title">Name this set</string>
@ -218,6 +219,7 @@
<string name="become_a_tester_description">Opt-in to our beta channel on Google Play and get early access to new features and bug fixes</string>
<string name="beta_opt_in_link">https://play.google.com/apps/testing/fr.free.nrw.commons</string>
<string name="_2fa_code">2FA Code</string>
<string name="email_auth_code">Email verification code</string>
<string name="logout_verification">Do you really want to logout?</string>
<string name="mediaimage_failed">Media Image Failed</string>
<string name="no_subcategory_found">No subcategories found</string>
@ -552,6 +554,7 @@ Upload your first media by tapping on the add button.</string>
<string name="dialog_box_text_nomination">Why should %1$s be deleted?</string>
<string name="review_is_uploaded_by">%1$s is uploaded by: %2$s</string>
<string name="default_description_language">Default description language</string>
<string name="secondary_description_languages">Secondary description languages</string>
<string name="delete_helper_show_deletion_title">Nominating for deletion</string>
<string name="delete_helper_show_deletion_title_success">Success</string>
<string name="delete_helper_show_deletion_message_if">Nominated %1$s for deletion.</string>

View file

@ -31,6 +31,13 @@
app:singleLineTitle="false"
android:title="@string/default_description_language" />
<!-- New Secondary Language Picker -->
<Preference
android:key="descriptionSecondaryLanguagesPref"
app:useSimpleSummaryProvider="true"
app:singleLineTitle="false"
android:title="@string/secondary_description_languages" />
<SwitchPreference
android:defaultValue="true"
android:key="displayNearbyCardView"

View file

@ -10,6 +10,8 @@ SELECT
(SAMPLE(?wikipediaArticle) AS ?wikipediaArticle)
(SAMPLE(?commonsArticle) AS ?commonsArticle)
(SAMPLE(?commonsCategory) AS ?commonsCategory)
(SAMPLE(?dateOfOfficialClosure) AS ?dateOfOfficialClosure)
(SAMPLE(?pointInTime) AS ?pointInTime)
WHERE {
SERVICE <https://query.wikidata.org/sparql> {
values ?item {
@ -19,11 +21,13 @@ WHERE {
# Get the label in the preferred language of the user, or any other language if no label is available in that language.
OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "${LANG}")}
${SECONDARYLABEL}
OPTIONAL {?item rdfs:label ?itemLabelAnyLanguage}
BIND(COALESCE(?itemLabelPreferredLanguage, ?itemLabelAnyLanguage, "?") as ?label)
# Get the description in the preferred language of the user, or any other language if no description is available in that language.
OPTIONAL {?item schema:description ?itemDescriptionPreferredLanguage. FILTER (lang(?itemDescriptionPreferredLanguage) = "${LANG}")}
${SECONDARYDESCRIPTION}
OPTIONAL {?item schema:description ?itemDescriptionAnyLanguage}
BIND(COALESCE(?itemDescriptionPreferredLanguage, ?itemDescriptionAnyLanguage, "?") as ?description)
@ -31,6 +35,7 @@ WHERE {
OPTIONAL {
?item p:P31/ps:P31 ?class.
OPTIONAL {?class rdfs:label ?classLabelPreferredLanguage. FILTER (lang(?classLabelPreferredLanguage) = "${LANG}")}
${SECONDARYCLASSLABEL}
OPTIONAL {?class rdfs:label ?classLabelAnyLanguage}
BIND(COALESCE(?classLabelPreferredLanguage, ?classLabelAnyLanguage, "?") as ?classLabel)
}
@ -45,6 +50,8 @@ WHERE {
# Get existence
OPTIONAL {?item wdt:P576 ?destroyed}
OPTIONAL {?item wdt:P582 ?endTime}
OPTIONAL {?item wdt:P3999 ?dateOfOfficialClosure}
OPTIONAL {?item wdt:P585 ?pointInTime}
# Get Commons category
OPTIONAL {?item wdt:P373 ?commonsCategory}

View file

@ -262,15 +262,4 @@ class UploadActivityUnitTests {
method.isAccessible = true
method.invoke(activity)
}
@Test
@Throws(Exception::class)
fun testOnBackPressed() {
val method: Method =
UploadActivity::class.java.getDeclaredMethod(
"onBackPressed",
)
method.isAccessible = true
method.invoke(activity)
}
}