mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 08:06:36 +00:00
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:
@@ -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([]);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -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]}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user