refactor(l10n): default strings to all targets

Most strings were imported with a platform-specific `targets` (usually
apple-only) just because that's where they happen to be used today. Drop the
restriction so they default to all targets and are available to KMP too; only
the four literal `%@…` format-composition keys stay apple-only.

Also fix the placeholder parser to tolerate C length modifiers (`%lld`), and
give progress_sending_to_count a proper int arg so it emits `%1$d` on both
platforms instead of a literal `%lld`.
This commit is contained in:
2026-07-20 10:24:54 +02:00
parent 3c0f29adb6
commit 4448822bfa
4 changed files with 49 additions and 116 deletions

View File

@@ -1684,7 +1684,7 @@
"en": { "en": {
"stringUnit": { "stringUnit": {
"state": "translated", "state": "translated",
"value": "Sending to %lld" "value": "Sending to %1$d"
} }
} }
} }

View File

@@ -60,8 +60,9 @@ export interface ParsedString {
args: Arg[]; args: Arg[];
} }
// Matches an escaped literal `%%` or a printf specifier (positional or bare). // Matches an escaped literal `%%` or a printf specifier (positional or bare),
const CONV = /%%|%(?:(\d+)\$)?([@sdif])/g; // tolerating C length modifiers like `%lld` / `%ld` on the integer forms.
const CONV = /%%|%(?:(\d+)\$)?l{0,2}([@sdif])/g;
function typeForConv(conv: string): ArgType { function typeForConv(conv: string): ArgType {
if (conv === "@" || conv === "s") return "string"; if (conv === "@" || conv === "s") return "string";

View File

@@ -67,108 +67,72 @@
}, },
"about_description": { "about_description": {
"context": "About screen: intro paragraph describing what VniDrop does.", "context": "About screen: intro paragraph describing what VniDrop does.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "VniDrop moves files and folders straight from one device to another over the network, without uploading them to any file-hosting service. Theres no account to create, and no copy of your transfer is left in the cloud once youre done." "en": "VniDrop moves files and folders straight from one device to another over the network, without uploading them to any file-hosting service. Theres no account to create, and no copy of your transfer is left in the cloud once youre done."
} }
}, },
"about_is_direct": { "about_is_direct": {
"context": "About screen, 'What VniDrop is' list: point about direct device-to-device transfer.", "context": "About screen, 'What VniDrop is' list: point about direct device-to-device transfer.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "A direct device-to-device transfer — your files go straight to the receiver." "en": "A direct device-to-device transfer — your files go straight to the receiver."
} }
}, },
"about_is_encrypted": { "about_is_encrypted": {
"context": "About screen, 'What VniDrop is' list: point about encrypted, verified connections.", "context": "About screen, 'What VniDrop is' list: point about encrypted, verified connections.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Connections are authenticated and end-to-end encrypted (via Iroh), and incoming files are verified by their content hash." "en": "Connections are authenticated and end-to-end encrypted (via Iroh), and incoming files are verified by their content hash."
} }
}, },
"about_is_in_control": { "about_is_in_control": {
"context": "About screen, 'What VniDrop is' list: point about controlling who receives.", "context": "About screen, 'What VniDrop is' list: point about controlling who receives.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "You decide who receives: approve each request, or open a transfer to anyone holding the invitation." "en": "You decide who receives: approve each request, or open a transfer to anyone holding the invitation."
} }
}, },
"about_is_no_account": { "about_is_no_account": {
"context": "About screen, 'What VniDrop is' list: point about being account-free.", "context": "About screen, 'What VniDrop is' list: point about being account-free.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Account-free — theres nothing to sign up for." "en": "Account-free — theres nothing to sign up for."
} }
}, },
"about_is_open": { "about_is_open": {
"context": "About screen, 'What VniDrop is' list: point about being open source.", "context": "About screen, 'What VniDrop is' list: point about being open source.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Open source, released under the Apache 2.0 license." "en": "Open source, released under the Apache 2.0 license."
} }
}, },
"about_is_title": { "about_is_title": {
"context": "About screen: section heading for the 'What VniDrop is' list.", "context": "About screen: section heading for the 'What VniDrop is' list.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "What VniDrop is" "en": "What VniDrop is"
} }
}, },
"about_isnt_cloud": { "about_isnt_cloud": {
"context": "About screen, 'What VniDrop isn't' list: point about not being cloud storage.", "context": "About screen, 'What VniDrop isn't' list: point about not being cloud storage.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Not cloud storage — no server holds your files, and nothing waits in the cloud after a transfer." "en": "Not cloud storage — no server holds your files, and nothing waits in the cloud after a transfer."
} }
}, },
"about_isnt_public": { "about_isnt_public": {
"context": "About screen, 'What VniDrop isn't' list: point about invitations not being public broadcasts.", "context": "About screen, 'What VniDrop isn't' list: point about invitations not being public broadcasts.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Not public broadcasting — an invitation is a private access link, not an announcement to everyone nearby." "en": "Not public broadcasting — an invitation is a private access link, not an announcement to everyone nearby."
} }
}, },
"about_isnt_sync": { "about_isnt_sync": {
"context": "About screen, 'What VniDrop isn't' list: point about not being sync/backup.", "context": "About screen, 'What VniDrop isn't' list: point about not being sync/backup.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Not a sync or backup service." "en": "Not a sync or backup service."
} }
}, },
"about_isnt_title": { "about_isnt_title": {
"context": "About screen: section heading for the 'What VniDrop isn't' list.", "context": "About screen: section heading for the 'What VniDrop isn't' list.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "What VniDrop isnt" "en": "What VniDrop isnt"
} }
}, },
"about_license_label": { "about_license_label": {
"context": "About screen: label for the license row.", "context": "About screen: label for the license row.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "License" "en": "License"
} }
@@ -181,63 +145,42 @@
}, },
"about_privacy_capability": { "about_privacy_capability": {
"context": "About screen, Privacy & security list: point that invitations are capabilities to share carefully.", "context": "About screen, Privacy & security list: point that invitations are capabilities to share carefully.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Invitations are capabilities. Treat a QR code, NFC tag, or .vnd file like a private access link and share it only with the people you intend — especially with “Anyone with this transfer.”" "en": "Invitations are capabilities. Treat a QR code, NFC tag, or .vnd file like a private access link and share it only with the people you intend — especially with “Anyone with this transfer.”"
} }
}, },
"about_privacy_deny": { "about_privacy_deny": {
"context": "About screen, Privacy & security list: point about denying unknown requests by default.", "context": "About screen, Privacy & security list: point about denying unknown requests by default.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Deny by default — VniDrop serves only the content of an active share and rejects unknown requests." "en": "Deny by default — VniDrop serves only the content of an active share and rejects unknown requests."
} }
}, },
"about_privacy_local": { "about_privacy_local": {
"context": "About screen, Privacy & security list: point about received files staying local and never overwriting.", "context": "About screen, Privacy & security list: point about received files staying local and never overwriting.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Received files are saved on your device and never silently overwrite an existing file." "en": "Received files are saved on your device and never silently overwrite an existing file."
} }
}, },
"about_privacy_policy_label": { "about_privacy_policy_label": {
"context": "About screen: label for the privacy policy row.", "context": "About screen: label for the privacy policy row.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Privacy policy" "en": "Privacy policy"
} }
}, },
"about_privacy_relay": { "about_privacy_relay": {
"context": "About screen, Privacy & security list: point explaining encrypted relays.", "context": "About screen, Privacy & security list: point explaining encrypted relays.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "If two devices cant connect directly, the encrypted connection may be relayed. Relays forward encrypted packets only; they never store your files." "en": "If two devices cant connect directly, the encrypted connection may be relayed. Relays forward encrypted packets only; they never store your files."
} }
}, },
"about_privacy_title": { "about_privacy_title": {
"context": "About screen: section heading for the Privacy & security list.", "context": "About screen: section heading for the Privacy & security list.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Privacy & security" "en": "Privacy & security"
} }
}, },
"about_tagline": { "about_tagline": {
"context": "About screen: short tagline under the app name.", "context": "About screen: short tagline under the app name.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Send files directly. Stay in control of who receives them." "en": "Send files directly. Stay in control of who receives them."
} }
@@ -338,9 +281,6 @@
}, },
"bug_report_contact_hint": { "bug_report_contact_hint": {
"context": "Bug report form: placeholder text in the contact email field.", "context": "Bug report form: placeholder text in the contact email field.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "name@example.com" "en": "name@example.com"
} }
@@ -365,9 +305,6 @@
}, },
"bug_report_expected_hint": { "bug_report_expected_hint": {
"context": "Bug report form: placeholder in the 'what did you expect' field.", "context": "Bug report form: placeholder in the 'what did you expect' field.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Describe what you expected to happen" "en": "Describe what you expected to happen"
} }
@@ -410,9 +347,6 @@
}, },
"bug_report_steps_hint": { "bug_report_steps_hint": {
"context": "Bug report form: placeholder in the steps-to-reproduce field.", "context": "Bug report form: placeholder in the steps-to-reproduce field.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "List the steps to reproduce the issue" "en": "List the steps to reproduce the issue"
} }
@@ -449,9 +383,6 @@
}, },
"bug_report_what_hint": { "bug_report_what_hint": {
"context": "Bug report form: placeholder in the what-happened field.", "context": "Bug report form: placeholder in the what-happened field.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Describe what happened" "en": "Describe what happened"
} }
@@ -974,12 +905,15 @@
}, },
"progress_sending_to_count": { "progress_sending_to_count": {
"context": "Transfer progress label: sending to N receivers. %lld = receiver count.", "context": "Transfer progress label: sending to N receivers. %lld = receiver count.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Sending to %lld" "en": "Sending to {count}"
} },
"args": [
{
"name": "count",
"type": "int"
}
]
}, },
"progress_share_ready": { "progress_share_ready": {
"context": "Transfer progress label: the share is ready.", "context": "Transfer progress label: the share is ready.",
@@ -1253,18 +1187,12 @@
}, },
"send_stop_sharing": { "send_stop_sharing": {
"context": "Transfer details: action to stop sharing a transfer.", "context": "Transfer details: action to stop sharing a transfer.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Stop sharing" "en": "Stop sharing"
} }
}, },
"send_stop_sharing_description": { "send_stop_sharing_description": {
"context": "Transfer details: confirmation body for stopping sharing.", "context": "Transfer details: confirmation body for stopping sharing.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "This stops the transfer and interrupts anyone currently downloading it. It stays in your history as Stopped." "en": "This stops the transfer and interrupts anyone currently downloading it. It stays in your history as Stopped."
} }
@@ -1361,99 +1289,66 @@
}, },
"storage_calculating": { "storage_calculating": {
"context": "Settings > Storage: placeholder while a size is being calculated.", "context": "Settings > Storage: placeholder while a size is being calculated.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Calculating…" "en": "Calculating…"
} }
}, },
"storage_delete_transfers": { "storage_delete_transfers": {
"context": "Settings > Storage: button to delete all transfer records.", "context": "Settings > Storage: button to delete all transfer records.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Delete all transfers" "en": "Delete all transfers"
} }
}, },
"storage_delete_transfers_description": { "storage_delete_transfers_description": {
"context": "Settings > Storage: confirmation body for deleting all transfer records.", "context": "Settings > Storage: confirmation body for deleting all transfer records.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "This clears all sent and received transfer records from your history. Your received files are not deleted. Note: the transfer engine keeps its stored content, so Transfer data may not decrease. This cant be undone." "en": "This clears all sent and received transfer records from your history. Your received files are not deleted. Note: the transfer engine keeps its stored content, so Transfer data may not decrease. This cant be undone."
} }
}, },
"storage_deleting": { "storage_deleting": {
"context": "Settings > Storage: progress label while transfers are being deleted.", "context": "Settings > Storage: progress label while transfers are being deleted.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Deleting…" "en": "Deleting…"
} }
}, },
"storage_footer": { "storage_footer": {
"context": "Settings > Storage: footer explaining how storage is managed.", "context": "Settings > Storage: footer explaining how storage is managed.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Transfer data is managed by the transfer engine — your history plus the content of files youve shared. The button below clears your transfer records; the engine keeps its stored content, so this value may not drop. Received files are your downloaded files and are never deleted here." "en": "Transfer data is managed by the transfer engine — your history plus the content of files youve shared. The button below clears your transfer records; the engine keeps its stored content, so this value may not drop. Received files are your downloaded files and are never deleted here."
} }
}, },
"storage_received_files": { "storage_received_files": {
"context": "Settings > Storage: label for the received-files size row.", "context": "Settings > Storage: label for the received-files size row.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Received files" "en": "Received files"
} }
}, },
"storage_temporary": { "storage_temporary": {
"context": "Settings > Storage: label for the temporary-files size row.", "context": "Settings > Storage: label for the temporary-files size row.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Temporary files" "en": "Temporary files"
} }
}, },
"storage_title": { "storage_title": {
"context": "Settings > Storage: section title.", "context": "Settings > Storage: section title.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Storage" "en": "Storage"
} }
}, },
"storage_total": { "storage_total": {
"context": "Settings > Storage: label for the total-size row.", "context": "Settings > Storage: label for the total-size row.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Total" "en": "Total"
} }
}, },
"storage_transfer_data": { "storage_transfer_data": {
"context": "Settings > Storage: label for the transfer-engine data size row.", "context": "Settings > Storage: label for the transfer-engine data size row.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "Transfer data" "en": "Transfer data"
} }
}, },
"storage_transfers_deleted": { "storage_transfers_deleted": {
"context": "Settings > Storage: toast confirming all transfers were deleted.", "context": "Settings > Storage: toast confirming all transfers were deleted.",
"targets": [
"apple"
],
"translations": { "translations": {
"en": "All transfers deleted" "en": "All transfers deleted"
} }

View File

@@ -1,7 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="about_bug_report">Report a bug</string> <string name="about_bug_report">Report a bug</string>
<string name="about_description">VniDrop moves files and folders straight from one device to another over the network, without uploading them to any file-hosting service. Theres no account to create, and no copy of your transfer is left in the cloud once youre done.</string>
<string name="about_is_direct">A direct device-to-device transfer — your files go straight to the receiver.</string>
<string name="about_is_encrypted">Connections are authenticated and end-to-end encrypted (via Iroh), and incoming files are verified by their content hash.</string>
<string name="about_is_in_control">You decide who receives: approve each request, or open a transfer to anyone holding the invitation.</string>
<string name="about_is_no_account">Account-free — theres nothing to sign up for.</string>
<string name="about_is_open">Open source, released under the Apache 2.0 license.</string>
<string name="about_is_title">What VniDrop is</string>
<string name="about_isnt_cloud">Not cloud storage — no server holds your files, and nothing waits in the cloud after a transfer.</string>
<string name="about_isnt_public">Not public broadcasting — an invitation is a private access link, not an announcement to everyone nearby.</string>
<string name="about_isnt_sync">Not a sync or backup service.</string>
<string name="about_isnt_title">What VniDrop isnt</string>
<string name="about_license_label">License</string>
<string name="about_privacy">Privacy policy</string> <string name="about_privacy">Privacy policy</string>
<string name="about_privacy_capability">Invitations are capabilities. Treat a QR code, NFC tag, or .vnd file like a private access link and share it only with the people you intend — especially with “Anyone with this transfer.”</string>
<string name="about_privacy_deny">Deny by default — VniDrop serves only the content of an active share and rejects unknown requests.</string>
<string name="about_privacy_local">Received files are saved on your device and never silently overwrite an existing file.</string>
<string name="about_privacy_policy_label">Privacy policy</string>
<string name="about_privacy_relay">If two devices cant connect directly, the encrypted connection may be relayed. Relays forward encrypted packets only; they never store your files.</string>
<string name="about_privacy_title">Privacy &amp; security</string>
<string name="about_tagline">Send files directly. Stay in control of who receives them.</string>
<string name="about_title">About</string> <string name="about_title">About</string>
<string name="appearance_auto_description">Match this devices light or dark appearance.</string> <string name="appearance_auto_description">Match this devices light or dark appearance.</string>
<string name="appearance_dark_mode">Dark mode</string> <string name="appearance_dark_mode">Dark mode</string>
@@ -14,20 +33,24 @@
<string name="approval_pending_count">%1$d requests waiting</string> <string name="approval_pending_count">%1$d requests waiting</string>
<string name="approval_request_body">%1$s wants to receive “%2$s”.</string> <string name="approval_request_body">%1$s wants to receive “%2$s”.</string>
<string name="battery_level_title">Battery level</string> <string name="battery_level_title">Battery level</string>
<string name="bug_report_contact_hint">name@example.com</string>
<string name="bug_report_contact_label">Contact email (optional)</string> <string name="bug_report_contact_label">Contact email (optional)</string>
<string name="bug_report_description">Tell us what went wrong. We attach device info and optional recent logs (with sensitive values redacted).</string> <string name="bug_report_description">Tell us what went wrong. We attach device info and optional recent logs (with sensitive values redacted).</string>
<string name="bug_report_device_section">Device information</string> <string name="bug_report_device_section">Device information</string>
<string name="bug_report_expected_hint">Describe what you expected to happen</string>
<string name="bug_report_expected_label">What did you expect?</string> <string name="bug_report_expected_label">What did you expect?</string>
<string name="bug_report_include_logs">Include recent logs</string> <string name="bug_report_include_logs">Include recent logs</string>
<string name="bug_report_include_logs_description">Helps us diagnose the issue. Sensitive values are redacted before sending.</string> <string name="bug_report_include_logs_description">Helps us diagnose the issue. Sensitive values are redacted before sending.</string>
<string name="bug_report_logs_size">Log attachment size</string> <string name="bug_report_logs_size">Log attachment size</string>
<string name="bug_report_missing_expected">Please describe what you expected.</string> <string name="bug_report_missing_expected">Please describe what you expected.</string>
<string name="bug_report_missing_what">Please describe what happened.</string> <string name="bug_report_missing_what">Please describe what happened.</string>
<string name="bug_report_steps_hint">List the steps to reproduce the issue</string>
<string name="bug_report_steps_label">Steps to reproduce (optional)</string> <string name="bug_report_steps_label">Steps to reproduce (optional)</string>
<string name="bug_report_submit">Submit report</string> <string name="bug_report_submit">Submit report</string>
<string name="bug_report_submit_failed">Could not submit the bug report. Try again later.</string> <string name="bug_report_submit_failed">Could not submit the bug report. Try again later.</string>
<string name="bug_report_submitted">Thanks — your bug report was recorded.</string> <string name="bug_report_submitted">Thanks — your bug report was recorded.</string>
<string name="bug_report_submitting">Submitting…</string> <string name="bug_report_submitting">Submitting…</string>
<string name="bug_report_what_hint">Describe what happened</string>
<string name="bug_report_what_label">What happened?</string> <string name="bug_report_what_label">What happened?</string>
<string name="button_approve">Approve</string> <string name="button_approve">Approve</string>
<string name="button_back">Back</string> <string name="button_back">Back</string>
@@ -114,6 +137,7 @@
<string name="progress_requesting_access">Requesting access</string> <string name="progress_requesting_access">Requesting access</string>
<string name="progress_saving">Saving</string> <string name="progress_saving">Saving</string>
<string name="progress_sending">Sending</string> <string name="progress_sending">Sending</string>
<string name="progress_sending_to_count">Sending to %1$d</string>
<string name="progress_share_ready">Ready to share</string> <string name="progress_share_ready">Ready to share</string>
<string name="progress_working">Working…</string> <string name="progress_working">Working…</string>
<string name="receive_choose_method_body">Choose the invitation method available to you.</string> <string name="receive_choose_method_body">Choose the invitation method available to you.</string>
@@ -157,6 +181,8 @@
<string name="send_new_transfer_title">New transfer</string> <string name="send_new_transfer_title">New transfer</string>
<string name="send_review_title">Review transfer</string> <string name="send_review_title">Review transfer</string>
<string name="send_selected_files_count">%1$d files selected</string> <string name="send_selected_files_count">%1$d files selected</string>
<string name="send_stop_sharing">Stop sharing</string>
<string name="send_stop_sharing_description">This stops the transfer and interrupts anyone currently downloading it. It stays in your history as Stopped.</string>
<string name="send_subtitle">Transfers youre sharing from this device.</string> <string name="send_subtitle">Transfers youre sharing from this device.</string>
<string name="send_title">Send</string> <string name="send_title">Send</string>
<string name="send_transfer_created">Transfer created.</string> <string name="send_transfer_created">Transfer created.</string>
@@ -172,6 +198,17 @@
<string name="status_preparing">Preparing</string> <string name="status_preparing">Preparing</string>
<string name="status_receiving">Receiving</string> <string name="status_receiving">Receiving</string>
<string name="status_stopped">Stopped</string> <string name="status_stopped">Stopped</string>
<string name="storage_calculating">Calculating…</string>
<string name="storage_delete_transfers">Delete all transfers</string>
<string name="storage_delete_transfers_description">This clears all sent and received transfer records from your history. Your received files are not deleted. Note: the transfer engine keeps its stored content, so Transfer data may not decrease. This cant be undone.</string>
<string name="storage_deleting">Deleting…</string>
<string name="storage_footer">Transfer data is managed by the transfer engine — your history plus the content of files youve shared. The button below clears your transfer records; the engine keeps its stored content, so this value may not drop. Received files are your downloaded files and are never deleted here.</string>
<string name="storage_received_files">Received files</string>
<string name="storage_temporary">Temporary files</string>
<string name="storage_title">Storage</string>
<string name="storage_total">Total</string>
<string name="storage_transfer_data">Transfer data</string>
<string name="storage_transfers_deleted">All transfers deleted</string>
<string name="transfer_activity_description">See important updates for this transfer</string> <string name="transfer_activity_description">See important updates for this transfer</string>
<string name="transfer_activity_title">Activity</string> <string name="transfer_activity_title">Activity</string>
<string name="transfer_delete_description">“%1$s” will stop being shared and its transfer history will be removed from this device.</string> <string name="transfer_delete_description">“%1$s” will stop being shared and its transfer history will be removed from this device.</string>