fix: translate the file sharing roles and label the files toolbar

The share sheet asked for files.share_role_* keys that no catalog has,
so Viewer, Editor and Manager stayed English everywhere; it now uses
the webmail's sharing.preset names and its "Share \"{name}\"" title.
The per-file menu, back and clear-selection buttons get screen reader
labels, and the "sharing is not available" error is translated.
This commit is contained in:
Linus Rath
2026-09-24 20:49:55 +02:00
parent d59c571d93
commit aab29da148
4 changed files with 48 additions and 7 deletions
+1
View File
@@ -375,6 +375,7 @@
"share_remove": "Remove access",
"share_role_custom": "Custom",
"share_search_placeholder": "Search by name or email",
"share_unavailable": "Sharing is not available on this device",
"shared_with": "Shared with",
"sharing_access": "Sharing & access",
"upload_unavailable": "The file picker is not installed in this build. Rebuild the app to enable uploads."
@@ -0,0 +1,23 @@
// Screens and sheets outside Settings and Contacts whose every user-visible
// string goes through t(). A literal English label, alert or JSX text
// sneaking back in stays English in every locale, so this scans the source.
import { describe, it, expect } from 'vitest';
import { join } from 'node:path';
import { findUntranslatedLiterals } from './untranslated-literals';
const SRC = join(__dirname, '..', '..');
// File (relative to src/) → texts that stay as they are on purpose.
const TRANSLATED_FILES: Record<string, string[]> = {
'screens/FilesScreen.tsx': [],
'components/files/ShareSheet.tsx': [],
'components/files/FilePreviewModal.tsx': [],
};
describe('translated screens', () => {
for (const [file, allow] of Object.entries(TRANSLATED_FILES)) {
it(`${file} has no hard-coded English text`, () => {
expect(findUntranslatedLiterals(join(SRC, file), allow)).toEqual([]);
});
}
});
+7 -4
View File
@@ -39,10 +39,11 @@ const FILE_PRESETS: Record<RolePreset, FileNodeRights> = {
},
};
// The webmail's sharing presets (same rights, same names).
const PRESET_LABEL_KEYS: Record<RolePreset, [string, string]> = {
read: ['files.share_role_viewer', 'Viewer'],
readWrite: ['files.share_role_editor', 'Editor'],
manager: ['files.share_role_manager', 'Manager'],
read: ['sharing.preset.read', 'Read only'],
readWrite: ['sharing.preset.readWrite', 'Read & write'],
manager: ['sharing.preset.manager', 'Manager'],
};
const PRESET_ORDER: RolePreset[] = ['read', 'readWrite', 'manager'];
@@ -159,7 +160,7 @@ export default function ShareSheet({ node, onClose, onChanged }: ShareSheetProps
<View style={styles.titleRow}>
<Icon size={18} color={c.textMuted} />
<Text style={styles.title} numberOfLines={1}>
{t('files.share', 'Share')} “{node.name}”
{t('sharing.title', 'Share "{name}"', { name: node.name })}
</Text>
</View>
{isFolder(node) ? (
@@ -188,6 +189,8 @@ export default function ShareSheet({ node, onClose, onChanged }: ShareSheetProps
onPress={() => void applyShare(principalId, FILE_PRESETS[p])}
disabled={busy}
style={[styles.chip, preset === p && styles.chipActive]}
accessibilityRole="button"
accessibilityState={{ selected: preset === p, disabled: busy }}
>
<Text
style={[styles.chipText, preset === p && styles.chipTextActive]}
+17 -3
View File
@@ -420,7 +420,7 @@ export default function FilesScreen() {
dest.create();
dest.write(bytes);
if (!(await Sharing.isAvailableAsync())) {
throw new Error('Sharing is not available on this device');
throw new Error(t('files.share_unavailable', 'Sharing is not available on this device'));
}
await Sharing.shareAsync(dest.uri, { mimeType: 'application/zip', dialogTitle: dest.name });
} catch (e) {
@@ -673,7 +673,13 @@ export default function FilesScreen() {
return (
<View style={styles.header}>
<View style={styles.headerRow}>
<Pressable onPress={clearSelection} hitSlop={8} style={styles.headerBtn}>
<Pressable
onPress={clearSelection}
hitSlop={8}
style={styles.headerBtn}
accessibilityRole="button"
accessibilityLabel={t('email_list.batch_actions.clear_selection', 'Clear selection')}
>
<X size={22} color={c.text} />
</Pressable>
<Text style={styles.title}>
@@ -711,7 +717,13 @@ export default function FilesScreen() {
<View style={styles.headerRow}>
<View style={styles.headerLeft}>
{canBack ? (
<Pressable onPress={goBack} hitSlop={8} style={styles.headerBtn}>
<Pressable
onPress={goBack}
hitSlop={8}
style={styles.headerBtn}
accessibilityRole="button"
accessibilityLabel={t('common.back', 'Back')}
>
<ChevronLeft size={22} color={c.text} />
</Pressable>
) : null}
@@ -890,6 +902,8 @@ export default function FilesScreen() {
}}
hitSlop={8}
style={styles.rowMore}
accessibilityRole="button"
accessibilityLabel={t('files.context_menu', 'Actions')}
>
<MoreVertical size={18} color={c.textMuted} />
</Pressable>