Skip to content

Commit e08b435

Browse files
authored
feat(i18n): localize the editor; bump to 1.3 (build 10) (#20)
Extends the string catalogs with editor and tools sections in all 13 languages: header status line (Page X of Y / saving / saved / failed), the unreadable-note recovery screen, start-blank and save-failed dialogs, export errors, insert-image menu, background sheet, page sidebar and toolbar accessibility labels, text box placeholders, and tool names. New notes are created with a localized default title, and BACKGROUND_LABELS is removed in favor of the catalog. Bumps the app to 1.3 (build 10) across app.json, build.gradle, project.pbxproj, and Info.plist for the release carrying iCloud backup, the onboarding backup choice, and localization. Verified on iPad simulator in German end to end: create-note sheet (Blanko/Liniert/Kariert/Gepunktet/Millimeterpapier), editor header 'Seite 1 von 1 · bereit', back button 'Bibliothek', default title 'Ohne Titel'. Typecheck enforces catalog completeness; 43/43 tests; Metro export bundles. Claude-Session: https://claude.ai/code/session_01YM3PBapEXHwuXY97Bibsqk
1 parent 2a7ae95 commit e08b435

26 files changed

Lines changed: 733 additions & 76 deletions

File tree

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ android {
9292
applicationId 'com.builderpro.opennotes'
9393
minSdkVersion rootProject.ext.minSdkVersion
9494
targetSdkVersion rootProject.ext.targetSdkVersion
95-
versionCode 9
96-
versionName "1.2"
95+
versionCode 10
96+
versionName "1.3"
9797

9898
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
9999
}

app.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"expo": {
33
"name": "OpenNotes",
44
"slug": "open-notes",
5-
"version": "1.2",
5+
"version": "1.3",
66
"scheme": "opennotes",
77
"orientation": "default",
88
"icon": "./assets/icon.png",
@@ -16,7 +16,7 @@
1616
"ios": {
1717
"bundleIdentifier": "com.builderpro.opennotes",
1818
"icon": "./assets/icon.png",
19-
"buildNumber": "9",
19+
"buildNumber": "10",
2020
"supportsTablet": true,
2121
"usesIcloudStorage": true,
2222
"entitlements": {
@@ -65,7 +65,7 @@
6565
},
6666
"android": {
6767
"package": "com.builderpro.opennotes",
68-
"versionCode": 9,
68+
"versionCode": 10,
6969
"adaptiveIcon": {
7070
"foregroundImage": "./assets/adaptive-icon.png",
7171
"backgroundColor": "#F7F7F4"

app/note/[id].tsx

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { exportNotebookAsPdf } from '../../src/services/exportService';
6363
import { recordSuccessfulNoteSave } from '../../src/services/lifecycleService';
6464
import { textBoxId, insertedElementId } from '../../src/utils/id';
6565
import { spacing } from '../../src/theme/spacing';
66+
import { t } from '../../src/i18n';
6667
import { typography } from '../../src/theme/typography';
6768
import type { NoteMetadata } from '../../src/types/note';
6869
import type { ToolDescriptor } from '../../src/utils/toolPalette';
@@ -335,12 +336,12 @@ export default function NoteScreen() {
335336

336337
const startBlankAfterLoadFailure = useCallback(() => {
337338
Alert.alert(
338-
'Start with a blank note?',
339-
'The existing contents of this note could not be read. Starting blank will replace them the next time the note saves.',
339+
t.editor.startBlankTitle,
340+
t.editor.startBlankBody,
340341
[
341-
{ text: 'Cancel', style: 'cancel' },
342+
{ text: t.common.cancel, style: 'cancel' },
342343
{
343-
text: 'Start blank',
344+
text: t.editor.startBlankConfirm,
344345
style: 'destructive',
345346
onPress: () => {
346347
setBodyLoadFailed(false);
@@ -715,13 +716,13 @@ export default function NoteScreen() {
715716
});
716717
if (!result.ok) {
717718
Alert.alert(
718-
'Export failed',
719-
result.error ?? 'Could not generate a PDF. Please try again.',
719+
t.editor.exportFailedTitle,
720+
result.error ?? t.editor.exportFailedBody,
720721
);
721722
}
722723
} catch (error) {
723724
if (__DEV__) console.warn('[NoteScreen] export failed', error);
724-
Alert.alert('Export failed', 'Could not generate a PDF. Please try again.');
725+
Alert.alert(t.editor.exportFailedTitle, t.editor.exportFailedBody);
725726
} finally {
726727
if (isMountedRef.current) setIsExporting(false);
727728
}
@@ -757,17 +758,17 @@ export default function NoteScreen() {
757758
return;
758759
}
759760
Alert.alert(
760-
"Couldn't save your note",
761-
'Your latest changes could not be written to storage. Leaving now will discard them.',
761+
t.editor.saveFailedTitle,
762+
t.editor.saveFailedBody,
762763
[
763-
{ text: 'Try again', onPress: () => void attempt() },
764+
{ text: t.editor.tryAgain, onPress: () => void attempt() },
764765
{
765-
text: 'Leave anyway',
766+
text: t.editor.leaveAnyway,
766767
style: 'destructive',
767768
onPress: () => navigateHome(),
768769
},
769770
{
770-
text: 'Stay',
771+
text: t.editor.stay,
771772
style: 'cancel',
772773
onPress: () => {
773774
exitRef.current = 'editing';
@@ -829,7 +830,7 @@ export default function NoteScreen() {
829830
]}
830831
>
831832
<Text style={[typography.title, { color: theme.colors.text, textAlign: 'center' }]}>
832-
Couldn't open this note
833+
{t.editor.couldNotOpenTitle}
833834
</Text>
834835
<Text
835836
style={[
@@ -842,26 +843,25 @@ export default function NoteScreen() {
842843
},
843844
]}
844845
>
845-
The note's contents could not be read from storage. Nothing has been
846-
changed; your data is still on this device.
846+
{t.editor.couldNotOpenBody}
847847
</Text>
848848
<Pressable
849849
onPress={retryLoad}
850850
style={[styles.errorButton, { backgroundColor: theme.colors.accent }]}
851851
>
852-
<Text style={[typography.headline, styles.errorButtonLabel]}>Try again</Text>
852+
<Text style={[typography.headline, styles.errorButtonLabel]}>{t.editor.tryAgain}</Text>
853853
</Pressable>
854854
<Pressable
855855
onPress={() => void handleBack()}
856856
style={[styles.errorButton, { backgroundColor: theme.colors.surfaceMuted }]}
857857
>
858858
<Text style={[typography.headline, { color: theme.colors.text }]}>
859-
Back to library
859+
{t.editor.backToLibrary}
860860
</Text>
861861
</Pressable>
862862
<Pressable onPress={startBlankAfterLoadFailure} style={styles.errorTextButton}>
863863
<Text style={[typography.subhead, { color: theme.colors.destructive }]}>
864-
Start with a blank note
864+
{t.editor.startBlankLink}
865865
</Text>
866866
</Pressable>
867867
</View>
@@ -881,7 +881,7 @@ export default function NoteScreen() {
881881
return (
882882
<View style={[styles.flex, { backgroundColor: theme.colors.background }]}>
883883
<EditorHeader
884-
title={metadata?.title ?? 'Untitled'}
884+
title={metadata?.title ?? t.common.untitled}
885885
status={autosaveStatus}
886886
currentPage={currentPageIndex}
887887
pageCount={Math.max(1, enginePages.length)}
@@ -983,16 +983,16 @@ export default function NoteScreen() {
983983

984984
<ItemActionsMenu
985985
visible={action?.kind === 'insertImage'}
986-
title="Insert image"
986+
title={t.editor.insertImageTitle}
987987
actions={[
988988
{
989989
key: 'library',
990-
label: 'Choose from Photos',
990+
label: t.editor.chooseFromPhotos,
991991
onPress: () => void handleInsertFromLibrary(),
992992
},
993993
{
994994
key: 'camera',
995-
label: 'Take Photo',
995+
label: t.editor.takePhoto,
996996
onPress: () => void handleInsertFromCamera(),
997997
},
998998
]}
@@ -1004,9 +1004,9 @@ export default function NoteScreen() {
10041004

10051005
<RenameDialog
10061006
visible={action?.kind === 'rename'}
1007-
title="Rename note"
1007+
title={t.library.renameNoteTitle}
10081008
initialValue={metadata?.title ?? ''}
1009-
placeholder="Note title"
1009+
placeholder={t.library.noteTitlePlaceholder}
10101010
onCancel={() => setAction(null)}
10111011
onConfirm={(value) => void handleRename(value)}
10121012
/>

ios/OpenNotes.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@
364364
CODE_SIGN_ENTITLEMENTS = OpenNotes/OpenNotes.entitlements;
365365
CODE_SIGN_IDENTITY = "Apple Development";
366366
CODE_SIGN_STYLE = Automatic;
367-
CURRENT_PROJECT_VERSION = 9;
367+
CURRENT_PROJECT_VERSION = 10;
368368
DEVELOPMENT_TEAM = U2CPXQV7AJ;
369369
ENABLE_BITCODE = NO;
370370
GCC_PREPROCESSOR_DEFINITIONS = (
@@ -377,7 +377,7 @@
377377
"$(inherited)",
378378
"@executable_path/Frameworks",
379379
);
380-
MARKETING_VERSION = 1.2;
380+
MARKETING_VERSION = 1.3;
381381
OTHER_LDFLAGS = (
382382
"$(inherited)",
383383
"-ObjC",
@@ -403,15 +403,15 @@
403403
CODE_SIGN_ENTITLEMENTS = OpenNotes/OpenNotes.entitlements;
404404
CODE_SIGN_IDENTITY = "Apple Development";
405405
CODE_SIGN_STYLE = Automatic;
406-
CURRENT_PROJECT_VERSION = 9;
406+
CURRENT_PROJECT_VERSION = 10;
407407
DEVELOPMENT_TEAM = U2CPXQV7AJ;
408408
INFOPLIST_FILE = OpenNotes/Info.plist;
409409
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
410410
LD_RUNPATH_SEARCH_PATHS = (
411411
"$(inherited)",
412412
"@executable_path/Frameworks",
413413
);
414-
MARKETING_VERSION = 1.2;
414+
MARKETING_VERSION = 1.3;
415415
OTHER_LDFLAGS = (
416416
"$(inherited)",
417417
"-ObjC",

ios/OpenNotes/Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<key>CFBundlePackageType</key>
3535
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
3636
<key>CFBundleShortVersionString</key>
37-
<string>1.2</string>
37+
<string>1.3</string>
3838
<key>CFBundleSignature</key>
3939
<string>????</string>
4040
<key>CFBundleURLTypes</key>
@@ -54,7 +54,7 @@
5454
</dict>
5555
</array>
5656
<key>CFBundleVersion</key>
57-
<string>9</string>
57+
<string>10</string>
5858
<key>ITSAppUsesNonExemptEncryption</key>
5959
<false/>
6060
<key>LSMinimumSystemVersion</key>

src/components/editor/BackgroundSheet.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { Sheet } from '../ui/Sheet';
55
import { useTheme } from '../../hooks/useTheme';
66
import { radius, spacing } from '../../theme/spacing';
77
import { typography } from '../../theme/typography';
8-
import { BACKGROUND_LABELS, BACKGROUND_TYPES } from '../../types/note';
8+
import { BACKGROUND_TYPES } from '../../types/note';
99
import type { BackgroundType } from '../../types/note';
10+
import { t } from '../../i18n';
1011

1112
export interface BackgroundSheetProps {
1213
visible: boolean;
@@ -34,7 +35,7 @@ export function BackgroundSheet({
3435
return (
3536
<Sheet visible={visible} onClose={onClose}>
3637
<Text style={[typography.title, { color: theme.colors.text, marginBottom: spacing.md }]}>
37-
Background
38+
{t.editor.backgroundTitle}
3839
</Text>
3940
<View style={styles.grid}>
4041
{BACKGROUND_TYPES.map((type) => {
@@ -70,7 +71,7 @@ export function BackgroundSheet({
7071
},
7172
]}
7273
>
73-
{BACKGROUND_LABELS[type]}
74+
{t.library.backgrounds[type]}
7475
</Text>
7576
</Pressable>
7677
);

src/components/editor/EditorHeader.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useTheme } from '../../hooks/useTheme';
66
import { spacing } from '../../theme/spacing';
77
import { typography } from '../../theme/typography';
88
import type { AutosaveStatus } from '../../hooks/useAutosave';
9+
import { t } from '../../i18n';
910

1011
export const EDITOR_HEADER_BAR_HEIGHT = 48;
1112

@@ -54,7 +55,7 @@ export function EditorHeader({
5455
style={({ pressed }) => [styles.sideButton, pressed && { opacity: 0.5 }]}
5556
>
5657
<Ionicons name="chevron-back" size={26} color={theme.colors.accent} />
57-
<Text style={[typography.body, { color: theme.colors.accent }]}>Library</Text>
58+
<Text style={[typography.body, { color: theme.colors.accent }]}>{t.library.backLabel}</Text>
5859
</Pressable>
5960

6061
<Pressable
@@ -75,14 +76,14 @@ export function EditorHeader({
7576
]}
7677
numberOfLines={1}
7778
>
78-
Page {currentPage + 1} of {pageCount} · {statusLabel(status)}
79+
{t.editor.pageStatus(currentPage + 1, pageCount, statusLabel(status))}
7980
</Text>
8081
</Pressable>
8182

8283
<View style={styles.rightActions}>
8384
<Pressable
8485
accessibilityRole="button"
85-
accessibilityLabel="Pages"
86+
accessibilityLabel={t.editor.pagesA11y}
8687
onPress={onTogglePageSidebar}
8788
disabled={pageCount < 2}
8889
hitSlop={10}
@@ -98,7 +99,7 @@ export function EditorHeader({
9899

99100
<Pressable
100101
accessibilityRole="button"
101-
accessibilityLabel="Export"
102+
accessibilityLabel={t.editor.exportA11y}
102103
onPress={onExport}
103104
disabled={isExporting}
104105
hitSlop={10}
@@ -122,15 +123,15 @@ export function EditorHeader({
122123
function statusLabel(status: AutosaveStatus): string {
123124
switch (status) {
124125
case 'idle':
125-
return 'ready';
126+
return t.editor.statusReady;
126127
case 'pending':
127-
return 'unsaved changes';
128+
return t.editor.statusPending;
128129
case 'saving':
129-
return 'saving…';
130+
return t.editor.statusSaving;
130131
case 'saved':
131-
return 'saved';
132+
return t.editor.statusSaved;
132133
case 'error':
133-
return 'save failed';
134+
return t.editor.statusError;
134135
}
135136
}
136137

src/components/editor/FloatingToolbar.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useTheme } from '../../hooks/useTheme';
2222
import { radius, spacing } from '../../theme/spacing';
2323
import { TOOL_DESCRIPTORS } from '../../utils/toolPalette';
2424
import type { SupportedTool, ToolDescriptor } from '../../utils/toolPalette';
25+
import { t } from '../../i18n';
2526

2627
const TOOLBAR_PAD = 6;
2728
const EDGE_MARGIN = 12;
@@ -520,22 +521,22 @@ export function FloatingToolbar({
520521
active={fingerDrawingEnabled}
521522
compact={isCompactHorizontal}
522523
accessibilityLabel={
523-
fingerDrawingEnabled ? 'Disable finger drawing' : 'Enable finger drawing'
524+
fingerDrawingEnabled ? t.editor.disableFingerDrawing : t.editor.enableFingerDrawing
524525
}
525526
onPress={onToggleFingerDrawing}
526527
theme={theme}
527528
/>
528529
<SmallIconButton
529530
iconName="arrow-undo-outline"
530531
compact={isCompactHorizontal}
531-
accessibilityLabel="Undo"
532+
accessibilityLabel={t.editor.undo}
532533
onPress={onUndo}
533534
theme={theme}
534535
/>
535536
<SmallIconButton
536537
iconName="arrow-redo-outline"
537538
compact={isCompactHorizontal}
538-
accessibilityLabel="Redo"
539+
accessibilityLabel={t.editor.redo}
539540
onPress={onRedo}
540541
theme={theme}
541542
/>

0 commit comments

Comments
 (0)