Merge branch 'commons-app:main' into issue#6084

This commit is contained in:
Sujal 2025-02-22 10:20:57 +05:30 committed by GitHub
commit 546437b186
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 323 additions and 81 deletions

View file

@ -0,0 +1,84 @@
name: Android CI Comment
on: [pull_request_target]
permissions:
issues: write
jobs:
comment:
name: Comment on PR with APK links
runs-on: ubuntu-latest
steps:
- name: Checkout base branch
uses: actions/checkout@v3
with:
ref: ${{ github.base_ref }}
- name: Download Run ID Artifact
uses: actions/download-artifact@v4
with:
name: run-id
- name: Read Run ID
id: read-run-id
run: echo "RUN_ID=$(cat run_id.txt)" >> $GITHUB_ENV
- name: Comment on PR with APK download links
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/github-script@v6
with:
script: |
try {
const token = process.env.GH_TOKEN;
if (!token) {
throw new Error('GITHUB_TOKEN is not set.');
}
const runId = "${{ env.RUN_ID }}";
if (!runId) {
throw new Error('Run ID not found.');
}
const { data: { artifacts } } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: runId
});
if (!artifacts || artifacts.length === 0) {
console.log('No artifacts found for this workflow run.');
return;
}
const betaArtifact = artifacts.find(artifact => artifact.name === "betaDebugAPK");
const prodArtifact = artifacts.find(artifact => artifact.name === "prodDebugAPK");
if (!betaArtifact || !prodArtifact) {
console.log('Could not find both Beta and Prod APK artifacts.');
console.log('Available artifacts:', artifacts.map(a => a.name).join(', '));
return;
}
const betaDownloadUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/suites/${runId}/artifacts/${betaArtifact.id}`;
const prodDownloadUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/suites/${runId}/artifacts/${prodArtifact.id}`;
const commentBody = `
📱 **APK for pull request is ready to see the changes** 📱
- [Download Beta APK](${betaDownloadUrl})
- [Download Prod APK](${prodDownloadUrl})
`;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
console.log('Successfully posted comment with APK download links');
} catch (error) {
console.error('Error in PR comment creation:', error);
core.setFailed(`Workflow failed: ${error.message}`);
}

View file

@ -3,7 +3,6 @@ name: Android CI
on: [push, pull_request, workflow_dispatch] on: [push, pull_request, workflow_dispatch]
permissions: permissions:
pull-requests: write
contents: read contents: read
actions: read actions: read
@ -108,63 +107,13 @@ jobs:
name: prodDebugAPK name: prodDebugAPK
path: app/build/outputs/apk/prod/debug/app-*.apk path: app/build/outputs/apk/prod/debug/app-*.apk
- name: Comment on PR with APK download links - name: Store Workflow Run ID
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
uses: actions/github-script@v6 run: echo "${{ github.run_id }}" > run_id.txt
- name: Upload Run ID as Artifact
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} name: run-id
script: | path: run_id.txt
try {
const token = process.env.GITHUB_TOKEN;
if (!token) {
throw new Error('GITHUB_TOKEN is not set. Please check workflow permissions.');
}
const { data: { artifacts } } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId
});
if (!artifacts || artifacts.length === 0) {
console.log('No artifacts found for this workflow run.');
return;
}
const betaArtifact = artifacts.find(artifact => artifact.name === "betaDebugAPK");
const prodArtifact = artifacts.find(artifact => artifact.name === "prodDebugAPK");
if (!betaArtifact || !prodArtifact) {
console.log('Could not find both Beta and Prod APK artifacts.');
console.log('Available artifacts:', artifacts.map(a => a.name).join(', '));
return;
}
const betaDownloadUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/suites/${context.runId}/artifacts/${betaArtifact.id}`;
const prodDownloadUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/suites/${context.runId}/artifacts/${prodArtifact.id}`;
const commentBody = `
📱 **APK for pull request is ready to see the changes** 📱
- [Download Beta APK](${betaDownloadUrl})
- [Download Prod APK](${prodDownloadUrl})
`;
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
console.log('Successfully posted comment with APK download links');
} catch (error) {
console.error('Error in PR comment creation:', error);
if (error.message.includes('GITHUB_TOKEN')) {
core.setFailed('Missing or invalid GITHUB_TOKEN. Please check repository secrets configuration.');
} else if (error.status === 403) {
core.setFailed('Permission denied. Please check workflow permissions in repository settings.');
} else {
core.setFailed(`Workflow failed: ${error.message}`);
}
}

View file

@ -175,8 +175,8 @@ dependencies {
testImplementation "androidx.work:work-testing:$work_version" testImplementation "androidx.work:work-testing:$work_version"
//Glide //Glide
implementation 'com.github.bumptech.glide:glide:4.12.0' implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
kaptTest "androidx.databinding:databinding-compiler:8.0.2" kaptTest "androidx.databinding:databinding-compiler:8.0.2"
kaptAndroidTest "androidx.databinding:databinding-compiler:8.0.2" kaptAndroidTest "androidx.databinding:databinding-compiler:8.0.2"

View file

@ -90,6 +90,41 @@ class Media constructor(
captions = captions, captions = captions,
) )
constructor(
captions: Map<String, String>,
categories: List<String>?,
filename: String?,
fallbackDescription: String?,
author: String?,
user: String?,
dateUploaded: Date? = Date(),
license: String? = null,
licenseUrl: String? = null,
imageUrl: String? = null,
thumbUrl: String? = null,
coordinates: LatLng? = null,
descriptions: Map<String, String> = emptyMap(),
depictionIds: List<String> = emptyList(),
categoriesHiddenStatus: Map<String, Boolean> = emptyMap()
) : this(
pageId = UUID.randomUUID().toString(),
filename = filename,
fallbackDescription = fallbackDescription,
dateUploaded = dateUploaded,
author = author,
user = user,
categories = categories,
captions = captions,
license = license,
licenseUrl = licenseUrl,
imageUrl = imageUrl,
thumbUrl = thumbUrl,
coordinates = coordinates,
descriptions = descriptions,
depictionIds = depictionIds,
categoriesHiddenStatus = categoriesHiddenStatus
)
/** /**
* Gets media display title * Gets media display title
* @return Media title * @return Media title

View file

@ -16,6 +16,7 @@ import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.OnGlobalLayoutListener import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Button import android.widget.Button
@ -405,9 +406,14 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
* Gets the height of the frame layout as soon as the view is ready and updates aspect ratio * Gets the height of the frame layout as soon as the view is ready and updates aspect ratio
* of the picture. * of the picture.
*/ */
view.post { view.post{
frameLayoutHeight = binding.mediaDetailFrameLayout.measuredHeight val width = binding.mediaDetailScrollView.width
updateAspectRatio(binding.mediaDetailScrollView.width) if (width > 0) {
frameLayoutHeight = binding.mediaDetailFrameLayout.measuredHeight
updateAspectRatio(width)
} else {
view.postDelayed({ updateAspectRatio(binding.root.width) }, 1)
}
} }
return view return view

View file

@ -185,10 +185,12 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
* or a fragment * or a fragment
*/ */
private void initProvider() { private void initProvider() {
if (getParentFragment() != null) { if (getParentFragment() instanceof MediaDetailProvider) {
provider = (MediaDetailProvider) getParentFragment(); provider = (MediaDetailProvider) getParentFragment();
} else { } else if (getActivity() instanceof MediaDetailProvider) {
provider = (MediaDetailProvider) getActivity(); provider = (MediaDetailProvider) getActivity();
} else {
throw new ClassCastException("Parent must implement MediaDetailProvider");
} }
} }

View file

@ -39,11 +39,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -51,6 +53,7 @@ import com.jakewharton.rxbinding2.view.RxView
import com.jakewharton.rxbinding3.appcompat.queryTextChanges import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.MapController.NearbyPlacesInfo import fr.free.nrw.commons.MapController.NearbyPlacesInfo
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
@ -67,6 +70,10 @@ import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermission
import fr.free.nrw.commons.location.LocationServiceManager import fr.free.nrw.commons.location.LocationServiceManager
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType
import fr.free.nrw.commons.location.LocationUpdateListener import fr.free.nrw.commons.location.LocationUpdateListener
import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.media.MediaDetailPagerFragment
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
import fr.free.nrw.commons.navtab.NavTab
import fr.free.nrw.commons.nearby.BottomSheetAdapter import fr.free.nrw.commons.nearby.BottomSheetAdapter
import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener
import fr.free.nrw.commons.nearby.CheckBoxTriStates import fr.free.nrw.commons.nearby.CheckBoxTriStates
@ -118,17 +125,25 @@ import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmentContract.View, class NearbyParentFragment : CommonsDaggerSupportFragment(),
WikidataP18EditListener, LocationUpdateListener, LocationPermissionCallback, ItemClickListener { NearbyParentFragmentContract.View,
WikidataP18EditListener,
LocationUpdateListener,
LocationPermissionCallback,
ItemClickListener,
MediaDetailPagerFragment.MediaDetailProvider {
var binding: FragmentNearbyParentBinding? = null var binding: FragmentNearbyParentBinding? = null
val mapEventsOverlay: MapEventsOverlay = MapEventsOverlay(object : MapEventsReceiver { val mapEventsOverlay: MapEventsOverlay = MapEventsOverlay(object : MapEventsReceiver {
@ -163,6 +178,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
@Named("default_preferences") @Named("default_preferences")
lateinit var applicationKvStore: JsonKvStore lateinit var applicationKvStore: JsonKvStore
@Inject
lateinit var mediaClient: MediaClient
lateinit var mediaDetails: MediaDetailPagerFragment
lateinit var media: Media
@Inject @Inject
lateinit var bookmarkLocationDao: BookmarkLocationsDao lateinit var bookmarkLocationDao: BookmarkLocationsDao
@ -716,6 +738,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
presenter?.attachView(this) presenter?.attachView(this)
registerNetworkReceiver() registerNetworkReceiver()
binding?.coordinatorLayout?.visibility = View.VISIBLE
binding?.map?.setMultiTouchControls(true)
binding?.map?.isClickable = true
if (isResumed && (activity as? MainActivity)?.activeFragment == ActiveFragment.NEARBY) { if (isResumed && (activity as? MainActivity)?.activeFragment == ActiveFragment.NEARBY) {
if (activity?.let { locationPermissionsHelper?.checkLocationPermission(it) } == true) { if (activity?.let { locationPermissionsHelper?.checkLocationPermission(it) } == true) {
locationPermissionGranted() locationPermissionGranted()
@ -1853,7 +1879,31 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
} }
fun backButtonClicked(): Boolean { fun backButtonClicked(): Boolean {
return presenter!!.backButtonClicked() if (::mediaDetails.isInitialized && mediaDetails.isVisible) {
removeFragment(mediaDetails)
binding?.coordinatorLayout?.visibility = View.VISIBLE
binding?.map?.setMultiTouchControls(true)
binding?.map?.isClickable = true
val transaction = childFragmentManager.beginTransaction()
val fragmentContainer = childFragmentManager.findFragmentById(R.id.coordinator_layout)
if (fragmentContainer != null) {
transaction.show(fragmentContainer)
}
transaction.commit()
childFragmentManager.executePendingTransactions()
(activity as? MainActivity)?.showTabs()
(activity as? MainActivity)?.supportActionBar?.setDisplayHomeAsUpEnabled(false)
return true
} else {
(activity as? MainActivity)?.setSelectedItemId(NavTab.NEARBY.code())
}
return presenter?.backButtonClicked() ?: false
} }
override fun onLocationPermissionDenied(toastMessage: String) { override fun onLocationPermissionDenied(toastMessage: String) {
@ -2299,7 +2349,23 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
bottomSheetAdapter!!.setClickListener(this) bottomSheetAdapter!!.setClickListener(this)
binding!!.bottomSheetDetails.bottomSheetRecyclerView.adapter = bottomSheetAdapter binding!!.bottomSheetDetails.bottomSheetRecyclerView.adapter = bottomSheetAdapter
updateBookmarkButtonImage(selectedPlace!!) updateBookmarkButtonImage(selectedPlace!!)
binding!!.bottomSheetDetails.icon.setImageResource(selectedPlace!!.label.icon)
selectedPlace?.pic?.substringAfterLast("/")?.takeIf { it.isNotEmpty() }?.let { imageName ->
Glide.with(binding!!.bottomSheetDetails.icon.context)
.clear(binding!!.bottomSheetDetails.icon)
Glide.with(binding!!.bottomSheetDetails.icon.context)
.load("https://commons.wikimedia.org/wiki/Special:Redirect/file/$imageName?width=25")
.placeholder(fr.free.nrw.commons.R.drawable.ic_refresh_24dp_nearby)
.error(selectedPlace!!.label.icon)
.into(binding!!.bottomSheetDetails.icon)
binding!!.bottomSheetDetails.icon.setOnClickListener {
handleMediaClick(imageName)
}
} ?: run {
binding!!.bottomSheetDetails.icon.setImageResource(selectedPlace!!.label.icon)
}
binding!!.bottomSheetDetails.title.text = selectedPlace!!.name binding!!.bottomSheetDetails.title.text = selectedPlace!!.name
binding!!.bottomSheetDetails.category.text = selectedPlace!!.distance binding!!.bottomSheetDetails.category.text = selectedPlace!!.distance
// Remove label since it is double information // Remove label since it is double information
@ -2354,6 +2420,101 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmen
} }
} }
private fun handleMediaClick(imageName: String) {
val decodedImageName = URLDecoder.decode(imageName, StandardCharsets.UTF_8.toString())
mediaClient.getMedia("File:$decodedImageName")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ mediaResponse ->
if (mediaResponse != null) {
// Create a Media object from the response
media = Media(
pageId = mediaResponse.pageId ?: UUID.randomUUID().toString(),
thumbUrl = mediaResponse.thumbUrl,
imageUrl = mediaResponse.imageUrl,
filename = mediaResponse.filename,
fallbackDescription = mediaResponse.fallbackDescription,
dateUploaded = mediaResponse.dateUploaded,
license = mediaResponse.license,
licenseUrl = mediaResponse.licenseUrl,
author = mediaResponse.author,
user = mediaResponse.user,
categories = mediaResponse.categories,
coordinates = mediaResponse.coordinates,
captions = mediaResponse.captions ?: emptyMap(),
descriptions = mediaResponse.descriptions ?: emptyMap(),
depictionIds = mediaResponse.depictionIds ?: emptyList(),
categoriesHiddenStatus = mediaResponse.categoriesHiddenStatus ?: emptyMap()
)
// Remove existing fragment before showing new details
if (::mediaDetails.isInitialized && mediaDetails.isAdded) {
removeFragment(mediaDetails)
}
showMediaDetails()
} else {
Timber.e("Fetched media is null for image: $decodedImageName")
}
}, { throwable ->
Timber.e(throwable, "Error fetching media for image: $decodedImageName")
})
}
private fun showMediaDetails() {
binding?.map?.setMultiTouchControls(false)
binding?.map?.isClickable = false
mediaDetails = MediaDetailPagerFragment.newInstance(false, true)
val transaction = childFragmentManager.beginTransaction()
val fragmentContainer = childFragmentManager.findFragmentById(R.id.coordinator_layout)
if (fragmentContainer != null) {
transaction.hide(fragmentContainer)
}
// Replace instead of add to ensure new fragment is used
transaction.replace(R.id.coordinator_layout, mediaDetails, "MediaDetailFragmentTag")
transaction.addToBackStack("Nearby_Parent_Fragment_Tag").commit()
childFragmentManager.executePendingTransactions()
(activity as? MainActivity)?.supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (mediaDetails.isAdded) {
mediaDetails.showImage(0)
} else {
Timber.e("Error: MediaDetailPagerFragment is NOT added")
}
}
override fun getMediaAtPosition(i: Int): Media? {
return media
}
override fun getTotalMediaCount(): Int {
return 2
}
override fun getContributionStateAt(position: Int): Int? {
return null
}
override fun refreshNominatedMedia(index: Int) {
if (this::mediaDetails.isInitialized && !binding?.map?.isClickable!! == true) {
removeFragment(mediaDetails)
showMediaDetails()
}
}
private fun removeFragment(fragment: Fragment) {
childFragmentManager
.beginTransaction()
.remove(fragment)
.commit()
childFragmentManager.executePendingTransactions()
}
private fun storeSharedPrefs(selectedPlace: Place) { private fun storeSharedPrefs(selectedPlace: Place) {
applicationKvStore!!.putJson<Place>(WikidataConstants.PLACE_OBJECT, selectedPlace) applicationKvStore!!.putJson<Place>(WikidataConstants.PLACE_OBJECT, selectedPlace)
val place = val place =

View file

@ -68,10 +68,10 @@
<string name="username">Декъашхочун цӀе</string> <string name="username">Декъашхочун цӀе</string>
<string name="password">Пароль</string> <string name="password">Пароль</string>
<string name="login_credential">Commons Beta тӀехь хьай цӀарца чугӀо</string> <string name="login_credential">Commons Beta тӀехь хьай цӀарца чугӀо</string>
<string name="login">ЧугӀо</string> <string name="login">Чувала</string>
<string name="forgot_password">Йицйелла пароль?</string> <string name="forgot_password">Йицйелла пароль?</string>
<string name="signup">ДӀайаздалар</string> <string name="signup">ДӀайаздалар</string>
<string name="logging_in_title">Системин чу дахар</string> <string name="logging_in_title">Системи чу валар</string>
<string name="logging_in_message">Дехар до, собарде…</string> <string name="logging_in_message">Дехар до, собарде…</string>
<string name="updating_caption_title">титраш а, йийцарш а карладохуш ду..</string> <string name="updating_caption_title">титраш а, йийцарш а карладохуш ду..</string>
<string name="updating_caption_message">Дехар до, собарде…</string> <string name="updating_caption_message">Дехар до, собарде…</string>

View file

@ -32,7 +32,7 @@
<string name="exit_location_picker">निकास स्थान चयनकर्ता</string> <string name="exit_location_picker">निकास स्थान चयनकर्ता</string>
<string name="submit">जमा करें</string> <string name="submit">जमा करें</string>
<string name="add_another_description">एक और विवरण जोड़ें</string> <string name="add_another_description">एक और विवरण जोड़ें</string>
<string name="add_new_contribution">नया योगदान</string> <string name="add_new_contribution">नया योगदान जोड़ें</string>
<string name="add_contribution_from_camera">कैमरे से योगदान जोड़ें</string> <string name="add_contribution_from_camera">कैमरे से योगदान जोड़ें</string>
<string name="add_contribution_from_photos">फ़ोटो से योगदान जोड़ें</string> <string name="add_contribution_from_photos">फ़ोटो से योगदान जोड़ें</string>
<string name="add_contribution_from_contributions_gallery">पिछले योगदान गैलरी से योगदान जोड़ें</string> <string name="add_contribution_from_contributions_gallery">पिछले योगदान गैलरी से योगदान जोड़ें</string>

View file

@ -150,7 +150,7 @@
<string name="provider_modifications">שינויים</string> <string name="provider_modifications">שינויים</string>
<string name="menu_upload_single">העלאה</string> <string name="menu_upload_single">העלאה</string>
<string name="categories_search_text_hint">חיפוש קטגוריות</string> <string name="categories_search_text_hint">חיפוש קטגוריות</string>
<string name="depicts_search_text_hint">חפשו פריטים שהמדיה שלכם מציגה (הר, טאג\' מהאל, וכו\')</string> <string name="depicts_search_text_hint">נא לחפש פריטים שהמדיה שלך מציגה (הר, טאג\' מהאל וכו\')</string>
<string name="menu_save_categories">שמירה</string> <string name="menu_save_categories">שמירה</string>
<string name="menu_overflow_desc">תפריט גלישה</string> <string name="menu_overflow_desc">תפריט גלישה</string>
<string name="refresh_button">רענון</string> <string name="refresh_button">רענון</string>
@ -851,7 +851,7 @@
<string name="account">חשבון</string> <string name="account">חשבון</string>
<string name="vanish_account">העלמת חשבון</string> <string name="vanish_account">העלמת חשבון</string>
<string name="account_vanish_request_confirm_title">אזהרת העלמת חשבון</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="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">כותרת</string>
<string name="caption_copied_to_clipboard">הכותרת הועתקה ללוח</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="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">ברכותינו, כל התמונות באלבום הזה הועלו או שסומנו לא להעלאה.</string>

View file

@ -56,7 +56,7 @@
<string name="menu_settings">امستنې</string> <string name="menu_settings">امستنې</string>
<string name="intent_share_upload_label">خونديځ ته راپورته کول</string> <string name="intent_share_upload_label">خونديځ ته راپورته کول</string>
<string name="upload_in_progress">راپورته کول جريان لري</string> <string name="upload_in_progress">راپورته کول جريان لري</string>
<string name="username">کارننوم</string> <string name="username">کارننوم</string>
<string name="password">پټنوم</string> <string name="password">پټنوم</string>
<string name="login_credential">خپل خونديځ بېټا ګڼون ته ورننوځئ</string> <string name="login_credential">خپل خونديځ بېټا ګڼون ته ورننوځئ</string>
<string name="login">ننوتل</string> <string name="login">ننوتل</string>

View file

@ -27,7 +27,6 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="all"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="all">
<string name="submit">{{Identical|Submit}}</string> <string name="submit">{{Identical|Submit}}</string>
<string name="nearby_all">{{identical|All}}</string> <string name="nearby_all">{{identical|All}}</string>
<string name="nearby_filter_toggle">יש קצת בלבול בתרגום עם להחליף למעלה, למרות שהתרגום באופן טכני נכון, המשמעות באנגלית היא החלפה בין מצבים (במקרה הזה, החלפת מצב פעיל) אז אפשר להשתמש בהפעלה כתרגום</string>
<string name="uploads_pending_notification_indicator">Status text about number of uploads left.\n* %1$d represents number of uploads left, including current one</string> <string name="uploads_pending_notification_indicator">Status text about number of uploads left.\n* %1$d represents number of uploads left, including current one</string>
<string name="contributions_subtitle">See the current issue [https://phabricator.wikimedia.org/T267142 T267142] tracked in Phabricator about the &lt;code&gt;&lt;nowiki&gt;|zero=&lt;/nowiki&gt;&lt;/code&gt; option currently not supported on Translatewiki.net with the custom &lt;code&gt;&lt;nowiki&gt;{{PLURAL}}&lt;/nowiki&gt;&lt;/code&gt; rules used by this project for Android, using a non-MediaWiki syntax.</string> <string name="contributions_subtitle">See the current issue [https://phabricator.wikimedia.org/T267142 T267142] tracked in Phabricator about the &lt;code&gt;&lt;nowiki&gt;|zero=&lt;/nowiki&gt;&lt;/code&gt; option currently not supported on Translatewiki.net with the custom &lt;code&gt;&lt;nowiki&gt;{{PLURAL}}&lt;/nowiki&gt;&lt;/code&gt; rules used by this project for Android, using a non-MediaWiki syntax.</string>
<string name="multiple_uploads_title">{{Identical|Upload}}</string> <string name="multiple_uploads_title">{{Identical|Upload}}</string>

View file

@ -4,6 +4,7 @@
* Alexandr Efremov * Alexandr Efremov
* Av6 * Av6
* Butko * Butko
* Cabadeck
* DDPAT * DDPAT
* Deltaspace * Deltaspace
* Dirruw'o * Dirruw'o
@ -177,6 +178,7 @@
<string name="categories_search_text_hint">Выбор категорий</string> <string name="categories_search_text_hint">Выбор категорий</string>
<string name="depicts_search_text_hint">Искать элементы, которые изображены на вашем изображении (гора, Тадж-Махал и т. д.)</string> <string name="depicts_search_text_hint">Искать элементы, которые изображены на вашем изображении (гора, Тадж-Махал и т. д.)</string>
<string name="menu_save_categories">Сохранить</string> <string name="menu_save_categories">Сохранить</string>
<string name="menu_overflow_desc">Дополнительное меню</string>
<string name="refresh_button">Обновить</string> <string name="refresh_button">Обновить</string>
<string name="display_list_button">Список</string> <string name="display_list_button">Список</string>
<string name="contributions_subtitle_zero">(Загрузок пока нет)</string> <string name="contributions_subtitle_zero">(Загрузок пока нет)</string>
@ -872,7 +874,11 @@
<string name="file_usages_container_heading">Использование файла</string> <string name="file_usages_container_heading">Использование файла</string>
<string name="title_activity_single_web_view">SingleWebViewActivity</string> <string name="title_activity_single_web_view">SingleWebViewActivity</string>
<string name="account">Учётная запись</string> <string name="account">Учётная запись</string>
<string name="vanish_account">Удалить учётную запись</string>
<string name="account_vanish_request_confirm_title">Предупреждение об удалении учётной записи</string>
<string name="caption">Подпись</string> <string name="caption">Подпись</string>
<string name="caption_copied_to_clipboard">Подпись скопирована в буфер обмена</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="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> </resources>

View file

@ -90,7 +90,7 @@
<string name="depicts_search_text_hint">Oza merkošiid maid du media govahallá (várri, Taj Mahal, jná.)</string> <string name="depicts_search_text_hint">Oza merkošiid maid du media govahallá (várri, Taj Mahal, jná.)</string>
<string name="menu_save_categories">Vurke</string> <string name="menu_save_categories">Vurke</string>
<string name="refresh_button">Ođasmahte</string> <string name="refresh_button">Ođasmahte</string>
<string name="display_list_button">Liste</string> <string name="display_list_button">Listu</string>
<string name="contributions_subtitle_zero">Eai vuos bajásluđemat)</string> <string name="contributions_subtitle_zero">Eai vuos bajásluđemat)</string>
<string name="categories_not_found">Ii oktage kategoriija heiven oktii %1$s:ain</string> <string name="categories_not_found">Ii oktage kategoriija heiven oktii %1$s:ain</string>
<string name="depictions_not_found">Ii oktage Wikidata mearkkuš heiven oktii %1$s:ain</string> <string name="depictions_not_found">Ii oktage Wikidata mearkkuš heiven oktii %1$s:ain</string>
@ -211,7 +211,7 @@
<string name="delete_search_dialog">Áiggutgo don duođaid dán ohcama sihkkut?</string> <string name="delete_search_dialog">Áiggutgo don duođaid dán ohcama sihkkut?</string>
<string name="delete">Sihko</string> <string name="delete">Sihko</string>
<string name="statistics">Statistihkka</string> <string name="statistics">Statistihkka</string>
<string name="level" fuzzy="true">Dássi</string> <string name="level">Dássi %d</string>
<string name="notifications">Almmuhusat</string> <string name="notifications">Almmuhusat</string>
<string name="list_sheet">Listu</string> <string name="list_sheet">Listu</string>
<string name="next">Čuovvovaš</string> <string name="next">Čuovvovaš</string>
@ -250,7 +250,7 @@
<string name="media_detail_in_all_languages">Buot gielaide</string> <string name="media_detail_in_all_languages">Buot gielaide</string>
<string name="description">Govvádus</string> <string name="description">Govvádus</string>
<string name="learn_more">LASSEDIEĐUT</string> <string name="learn_more">LASSEDIEĐUT</string>
<string name="menu_view_user_page" fuzzy="true">Čájet geavaheaddjisiiddu</string> <string name="menu_view_user_page">Čájet geavaheaddjidieđuid</string>
<string name="edit_categories">Rievdat kategoriijaid</string> <string name="edit_categories">Rievdat kategoriijaid</string>
<string name="advanced_options">Lasseásahusat</string> <string name="advanced_options">Lasseásahusat</string>
<string name="apply">Geavat</string> <string name="apply">Geavat</string>