halkyon/halkyon_learn_flutter/lib/l10n/app_localizations_en.dart

7262 lines
166 KiB
Dart
Raw Normal View History

feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
// ignore: unused_import
import 'package:intl/intl.dart' as intl;
import 'app_localizations.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get appTitle => 'Halkyon Learn';
@override
String get launchBrand => 'Halkyon';
@override
String get launchProduct => 'Learn';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get shellTabHome => 'Home';
@override
String get shellTabLibrary => 'Library';
@override
String get shellTabExplore => 'Explore';
@override
String get shellTabProfile => 'Profile';
@override
String get homeTitle => 'Home';
@override
String get libraryTitle => 'Library';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String get libraryCreateStudyset => 'Create studyset';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String get libraryEmpty => 'No studysets yet';
@override
String get libraryUnreadable => 'Couldn\'t read your studysets.';
@override
String get libraryArchivedSection => 'Archived';
@override
String get librarySearchTooltip => 'Search';
@override
String get librarySearchHint => 'Search studysets';
@override
String get librarySearchClearTooltip => 'Clear query';
@override
String get librarySearchCloseTooltip => 'Close search';
@override
String get librarySearchNoMatches => 'No studysets match your search';
@override
String get libraryStudysetArchived => 'Studyset archived';
@override
String get libraryStudysetUnarchived => 'Studyset unarchived';
@override
String get libraryStudysetDeleted => 'Moved to Recently deleted';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String get libraryDeletedSection => 'Recently deleted';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String get libraryDeletedNote =>
'Deleted studysets are kept for 14 days, with everything inside them. After that, they are deleted for good.';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String libraryDeletedDaysLeft(int days) {
String _temp0 = intl.Intl.pluralLogic(
days,
locale: localeName,
other: '$days days left',
one: '1 day left',
zero: 'Deleted for good soon',
);
return '$_temp0';
}
@override
String get libraryRestore => 'Restore';
@override
String get libraryDeleteNow => 'Delete now';
@override
String get libraryStudysetRestored => 'Studyset restored';
@override
String get libraryDeleteForGoodTitle => 'Delete for good?';
@override
String libraryDeleteForGoodMessage(String title) {
return '“$title” is deleted with everything inside it and all their cards, on all your devices. This can\'t be undone.';
}
@override
String get libraryDeleteForGoodConfirm => 'Delete for good';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String get studysetSheetCreateTitle => 'Create studyset';
@override
String get studysetSheetEditTitle => 'Edit studyset';
@override
String get studysetSheetCreate => 'Create';
@override
String get studysetSheetSave => 'Save';
@override
String get studysetSheetTitleLabel => 'Title';
@override
String get studysetSheetSecondaryLabel => 'Secondary title';
@override
String get studysetSheetSecondaryHelper =>
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
'Shown as a small label above the title on cards and the studyset\'s header. Paths use it to label tiers (Challenge, Milestone, Lesson).';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
String get studysetSheetColor => 'Colour';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
@override
String get studysetSheetDiscardTitle => 'Discard changes?';
@override
String get studysetSheetDiscardMessage =>
'You have unsaved changes to this studyset. Discard them and close the form?';
@override
String get studysetSheetDiscardConfirm => 'Discard';
@override
String paletteColorName(String name) {
String _temp0 = intl.Intl.selectLogic(
name,
{
'carrot': 'Carrot',
'cerise': 'Cerise',
'cobalt': 'Cobalt',
'copper': 'Copper',
'enzian': 'Enzian',
'fern': 'Fern',
'forest': 'Forest',
'ocean': 'Ocean',
'olive': 'Olive',
'pacific': 'Pacific',
'pickle': 'Pickle',
'pine': 'Pine',
'pink': 'Pink',
'plum': 'Plum',
'purple': 'Purple',
'reef': 'Reef',
'sahara': 'Sahara',
'slateblue': 'Slate blue',
'soil': 'Soil',
'strawberry': 'Strawberry',
'other': '$name',
},
);
return '$_temp0';
}
@override
String colorPickerLabel(String color) {
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
return 'Colour: $color';
feat(app): the library: a grid of studyset cards, search and archive The library tab becomes a grid of studyset cards (up to 400 dp wide, all equally tall), starred ones first, and the archive as a section of its own below, closed until opened and counting what rests there. It lists the top level: studysets without a parent, those whose parent is gone, and those in a cycle (ADR 0004). Each card's progress follows the studysets and every change of a card, here or synced, and is worked out again after a study session; a slow, older working out never overwrites a newer one. - Search: a field below the app bar filters by title and secondary title, opens the archive while it finds something there, and says when nothing matches; closing it shows everything again. - Creating and editing: a sheet with the title, the secondary title (typed in capitals, as every surface shows it) and the colour, picked from the palette's twenty, whose names are localised; a new studyset is cobalt, and an existing one keeps its colour, even one only a newer app version knows, until another is picked. Create waits for a title. Closing the sheet with changes asks first. It can't be swiped away: a bottom sheet swiped away closes without asking its PopScope (Flutter issue 152120, still open), so such sheets have no drag handle and close by their close button, the back gesture or a tap beside them, which all ask. - Archiving doesn't ask, since nothing is lost; it offers to undo, as does unarchiving. Deleting asks, then deletes the studyset with its cards, each blanked first. - The create button floats in compact windows and slides away while reading onwards; from medium windows on it sits in the app bar. The old list and its title-only dialog are gone; the studyset screen's tests create studysets through the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:53:51 +02:00
}
@override
String studysetCountFlashcards(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards',
one: '1 flashcard',
);
return '$_temp0';
}
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
@override
String studysetCountNotes(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count notes',
one: '1 note',
);
return '$_temp0';
}
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
@override
String studysetCountFiles(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count files',
one: '1 file',
);
return '$_temp0';
}
@override
String get libraryRefresh => 'Refresh';
@override
String get libraryOptions => 'Library options';
@override
String get libraryImport => 'Import';
@override
String get libraryImportAnkiHint =>
'Becomes a studyset of its own, from an .apkg or .colpkg file';
feat(archive): import a studyset with everything in it and its progress Your call (2026-10-11): an archive, imported from the Library's Import, to bring a library from elsewhere as one's own, and later to restore backups and move studysets between instances before federation. An archive is a .zip: archive.json says its format; the studyset is laid out as a release (ADR 0005, rule 3), so the release copier writes it, now also unlinked from any publication (ReleaseCopier.write), its cards marked "archive" as imported ones are, handing back each item's new id. A card may name a dictionary entry instead of its payload: the import makes it from the instance's dictionary as adding from it does, with today's entry, examples and tags, and leaves out (counting) one the instance lacks; for that, DictionaryEndpoint.entries gives entries by their ids, at most 1,000 at once. reviews.json lists reviews of the archive's cards; they go into the person's review log once the cards are in, so every card's schedule goes on from them. The sheet says what the archive holds ("Japanese" holds: 12 studysets, 7,745 flashcards, 3 paths, 20,317 reviews), imports once confirmed, busy meanwhile, and opens the studyset made. Not an archive, a newer format or an unreadable one says so and imports nothing. No export into the format yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 16:39:07 +02:00
@override
String get libraryImportArchive => 'Halkyon archive';
@override
String get libraryImportArchiveHint =>
'A studyset with everything in it and its progress, from a .zip file';
@override
String get archiveFileType => 'Halkyon archive';
@override
String get archiveImportTitle => 'Import archive';
@override
String get archiveImportReading => 'Reading the archive…';
@override
String archiveImportHolds(String title) {
return '“$title” holds:';
}
@override
String archiveCountStudysets(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count studysets',
one: '1 studyset',
);
return '$_temp0';
}
@override
String archiveCountReviews(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count reviews',
one: '1 review',
);
return '$_temp0';
}
@override
String get archiveImportConfirm => 'Import';
@override
String archiveImported(String cards, String reviews) {
return 'Imported: $cards and $reviews.';
}
@override
String archiveImportMissingEntries(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count words aren\'t',
one: '1 word isn\'t',
);
return '$_temp0 in this instance\'s dictionary and stayed out.';
}
@override
String get archiveNotAnArchive => 'This file isn\'t a Halkyon archive.';
@override
String get archiveNewerFormat =>
'This archive comes from a newer version of the app. Update the app to import it.';
@override
String get archiveUnreadable => 'This archive can\'t be read.';
@override
String get archiveImportBrokeOff =>
'The import broke off. Check the connection and try again.';
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
@override
String get libraryRetry => 'Retry';
@override
String get studysetNoContent => 'No content yet';
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetEmpty => 'Empty';
@override
String get studysetActionReview => 'Review';
@override
String get studysetActionStudy => 'Study';
@override
String get studysetActionAdd => 'Add content';
@override
String get studysetArchived => 'Archived';
@override
String get studysetStar => 'Star';
@override
String get studysetUnstar => 'Unstar';
@override
String get studysetByYou => 'By you';
@override
String studysetByAuthor(String author) {
return 'By $author';
}
@override
String get studysetByOthers => 'A copy';
@override
String get studysetVisibilityPrivate => 'Private';
@override
String get studysetVisibilityPublic => 'Public';
@override
String get studysetOptions => 'Options';
@override
String get studysetBibliography => 'Bibliography';
@override
String get studysetBibliographyIntro =>
'Works cited across this studyset, in the citation style chosen in Settings.';
@override
String get studysetMenuEdit => 'Edit';
@override
String get studysetMenuArchive => 'Archive';
@override
String get studysetMenuUnarchive => 'Unarchive';
@override
String get studysetMenuDelete => 'Delete';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetMenuMove => 'Move to…';
@override
String get studysetTabFlashcards => 'Flashcards';
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
@override
String get studysetTabNotes => 'Notes';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get studysetTabPaths => 'Paths';
@override
String get studysetCreatePath => 'Create a path';
@override
String get pathImportMenu => 'Import a path (JSON)';
@override
String get pathImportFileType => 'Learning path (JSON)';
@override
String pathImported(String title, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Imported \"$title\" with $count steps.',
one: 'Imported \"$title\" with 1 step.',
);
return '$_temp0';
}
@override
String pathImportSkipped(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other:
'$count blocks were left out: images and animations aren\'t imported.',
one: '1 block was left out: images and animations aren\'t imported.',
);
return '$_temp0';
}
@override
String pathImportFailed(String reason) {
return 'Couldn\'t import this file. $reason';
}
@override
String pathImportFileFailed(String file, String reason) {
return 'Couldn\'t import $file. $reason';
}
@override
String pathImportReading(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Reading $count files…',
one: 'Reading the file…',
);
return '$_temp0';
}
@override
String pathImportStoring(int index, int count, String title) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Importing path $index of $count: “$title”…',
one: 'Importing “$title”…',
);
return '$_temp0';
}
@override
String pathsImported(int count, int steps) {
String _temp0 = intl.Intl.pluralLogic(
steps,
locale: localeName,
other: 'Imported $count paths with $steps steps.',
one: 'Imported $count paths with 1 step.',
zero: 'Imported $count paths without steps.',
);
return '$_temp0';
}
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get pathImportUnreadable => 'It isn\'t text in UTF-8.';
@override
String get pathsEmpty => 'No paths yet';
@override
String get pathsEmptyHint =>
'A path leads through a lesson, step by step. Use \"Add material → Create a path\" to write one, or \"⋮ → Import content\" to import one.';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get pathOptions => 'Path options';
@override
String get pathEdit => 'Edit';
@override
String get pathResetProgress => 'Reset progress';
@override
String get pathProgressReset => 'Progress reset';
@override
String get pathResetTitle => 'Reset progress?';
@override
String get pathResetMessage =>
'All your progress through this path will be cleared. You can undo this for a few seconds.';
@override
String get pathResetConfirm => 'Reset';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get pathDelete => 'Delete';
@override
String get pathDeleteTitle => 'Delete path?';
@override
String get pathDeleteMessage =>
'This path and its steps will be removed from the studyset. You can undo this for a few seconds.';
@override
String get pathDeleteConfirm => 'Delete';
@override
String get pathDeleted => 'Path deleted';
@override
String get pathCreateTitle => 'New path';
@override
String get pathCreate => 'Create';
@override
String get pathTitleLabel => 'Title';
@override
String get pathSecondaryTitleLabel => 'Secondary title';
@override
String get pathSecondaryTitleHint =>
'E.g. the title in the language you learn';
@override
String get pathEditTitle => 'Edit path';
@override
String get pathDescription => 'Description';
@override
String get pathAddDescription => 'Add a description';
@override
String get pathDescriptionEditTitle => 'Path description';
@override
String get pathAddTags => 'Add tags';
@override
String get pathTags => 'Tags';
@override
String get pathEditTags => 'Edit tags';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get pathSteps => 'Steps';
@override
String get pathStepsEmpty => 'No steps yet';
@override
String get pathStepsEmptyHint =>
'Pick a step type to start writing the lesson.';
@override
String get pathAddStep => 'Add step';
@override
String get pathReorder => 'Reorder';
@override
String get pathReorderDone => 'Done';
@override
String get pathStepDelete => 'Delete step';
@override
String get pathStepDeleted => 'Step deleted';
@override
String get stepUntitled => 'Untitled step';
@override
String get stepKindInfo => 'Info';
@override
String get stepKindSingleChoice => 'Single choice';
@override
String get stepKindMultipleChoice => 'Multiple choice';
@override
String get stepKindDragAndDrop => 'Drag and drop';
@override
String get stepKindFillInTheBlank => 'Fill in the blank';
@override
String get stepEditTitle => 'Edit step';
@override
String get stepTitleHint => 'Title';
@override
String get stepOptions => 'Options';
@override
String stepOptionHint(int number) {
return 'Option $number';
}
@override
String get stepAddOption => 'Add option';
@override
String get stepRemoveOption => 'Remove option';
@override
String get stepCorrectOption => 'The right one';
@override
String get stepCorrectOptions => 'Right ones';
@override
String get stepCategories => 'Categories';
@override
String stepCategoryHint(int number) {
return 'Category $number';
}
@override
String get stepAddCategory => 'Add category';
@override
String get stepRemoveCategory => 'Remove category';
@override
String get stepItems => 'Items';
@override
String stepItemHint(int number) {
return 'Item $number';
}
@override
String get stepAddItem => 'Add item';
@override
String get stepRemoveItem => 'Remove item';
@override
String get stepItemCategory => 'Belongs to';
@override
String get stepPassage => 'Passage';
@override
String get stepPassageHint =>
'Type ___ (three underscores) wherever a word should be dropped in.';
@override
String stepBlankHint(int number) {
return 'Word for gap $number';
}
@override
String get stepIncomplete =>
'Not ready to answer yet: fill in every field and choose what is right.';
@override
String get stepFeedbackRight => 'Feedback after a right answer';
@override
String get stepFeedbackWrong => 'Feedback after a wrong answer';
@override
String get stepFeedbackNone => 'None';
@override
String get sources => 'Sources';
@override
String sourcesCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count sources',
one: '1 source',
);
return '$_temp0';
}
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourcesNone => 'No sources cited yet.';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourcesEdit => 'Edit sources';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourcesEditDone => 'Done editing';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourceAdd => 'Add a source';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourceEdit => 'Edit source';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourceRemove => 'Remove source';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get sourceRemoved => 'Source removed';
@override
String get sourceSheetAdd => 'Add';
@override
String get sourceAuthors => 'Authors';
@override
String get sourceAuthorsHint =>
'Full names, one per line, e.g. Mark A. McDaniel';
@override
String get sourceYear => 'Year';
@override
String get sourceYearHint => 'e.g. 2014';
@override
String get sourceTitle => 'Title';
@override
String get sourceTitleHint => 'Title of the work';
@override
String get sourceContainer => 'Published in';
@override
String get sourceContainerHint =>
'Journal, book or website it is part of, with pages';
@override
String get sourceContainerHelper =>
'Leave empty for a work of its own, such as a book.';
@override
String get sourcePublisher => 'Publisher';
@override
String get sourcePublisherHint => 'e.g. Belknap Press';
@override
String get sourceIsbn => 'ISBN';
@override
String get sourceIsbnHint => 'e.g. 978-0-674-72901-8';
@override
String get sourceUrl => 'Link';
@override
String get sourceUrlHint => 'https://…';
@override
String sourceImportedAs(String citation) {
return 'Imported as: $citation';
}
@override
String sourceIsbnLine(String isbn) {
return 'ISBN: $isbn';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
}
@override
String get sourceAvailableAt => 'Available at:';
@override
String get sourceEmpty => 'Empty reference';
@override
String get sourceDiscardTitle => 'Discard changes?';
@override
String get sourceDiscardMessage =>
'What you typed for this source will be lost.';
@override
String get sourceDiscardConfirm => 'Discard';
@override
String get sourceKind => 'Kind of source';
@override
String get sourceKindWork => 'Book or article';
@override
String get sourceKindWebsite => 'Website';
@override
String get sourceKindVideo => 'YouTube video';
@override
String get sourceWebsiteName => 'Website';
@override
String get sourceWebsiteNameHint => 'Its name, e.g. Wikipedia';
@override
String get sourcePageTitleHint => 'Title of the page';
@override
String get sourceChannel => 'Channel';
@override
String get sourceChannelHint => 'Who published it on YouTube';
@override
String get sourceVideoTitleHint => 'Title of the video';
@override
String get sourceVideoLink => 'Link to the video';
@override
String get sourceVideoLinkInvalid => 'Not a link to a YouTube video';
@override
String get sourcePlay => 'Play video';
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
@override
String get stepConflict =>
'This step was changed differently on another device. Keep one version to go on writing.';
@override
String get stepFieldTitle => 'Title';
@override
String get stepFieldBody => 'Text';
@override
String get stepFieldExercise => 'Exercise';
@override
String get stepFieldRightFeedback => 'Feedback after a right answer';
@override
String get stepFieldWrongFeedback => 'Feedback after a wrong answer';
@override
String get stepFieldSources => 'Sources';
@override
String get runnerClose => 'Close';
@override
String get runnerEditStep => 'Edit step';
@override
String get runnerBack => 'Back';
@override
String get runnerCheck => 'Check answers';
@override
String get runnerContinue => 'Continue';
@override
String get runnerFinish => 'Finish';
@override
String get runnerRight => 'Correct!';
@override
String get runnerWrong => 'Not quite';
@override
String get runnerAlreadyRight => 'You already answered this correctly.';
@override
String get runnerSelectAll => 'Select all that apply.';
@override
String get runnerDragHint =>
'Drag each item into the category it belongs to, or tap it and then the category. Tap a placed item to take it back.';
@override
String get runnerBlankHint =>
'Drag a word into each gap, or tap it and then the gap. Tap a placed word to take it back.';
@override
String get runnerEmpty => 'This path has no steps yet.';
@override
String get runnerFinished => 'You finished this path';
@override
String runnerFinishedSteps(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count steps complete.',
one: '1 step complete.',
);
return '$_temp0';
}
@override
String get runnerNextPath => 'Continue to next path';
@override
String get runnerBackToStudyset => 'Back to studyset';
@override
String studysetCountPaths(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count paths',
one: '1 path',
);
return '$_temp0';
}
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
@override
String get studysetCreateNote => 'Create a note';
@override
String get notesEmpty => 'No notes yet';
@override
String get notesEmptyHint =>
'Use \"Add material → Create a note\" to write one.';
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
@override
String get noteUntitled => 'Untitled note';
@override
String noteEdited(String when) {
return 'Edited $when';
}
@override
String get timeJustNow => 'just now';
@override
String timeMinutesAgo(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count minutes ago',
one: '1 minute ago',
);
return '$_temp0';
}
@override
String timeHoursAgo(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count hours ago',
one: '1 hour ago',
);
return '$_temp0';
}
@override
String timeDaysAgo(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count days ago',
one: 'yesterday',
);
return '$_temp0';
}
@override
String get noteOptions => 'Note options';
@override
String get noteEdit => 'Edit';
@override
String get noteDelete => 'Delete';
@override
String get noteDeleteTitle => 'Delete note?';
@override
String get noteDeleteMessage =>
'This note will be removed from the studyset. You can undo this for a few seconds.';
@override
String get noteDeleteConfirm => 'Delete';
@override
String get noteDeleted => 'Note deleted';
@override
String get noteTitleHint => 'Title';
@override
String get noteEditing => 'Editing…';
@override
String get noteSaving => 'Saving…';
@override
String get noteSaved => 'Saved';
@override
String get noteSaveFailed => 'Save failed';
@override
String get noteConflict =>
'This note was changed differently on another device. Keep one version to go on writing.';
@override
String get noteFieldTitle => 'Title';
@override
String get noteFieldBody => 'Text';
@override
String get noteKeepThis => 'Keep this';
@override
String get noteAddTags => 'Add tags';
@override
String get noteEditTags => 'Edit tags';
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetFlashcardsEmpty => 'No flashcards yet';
@override
String get studysetFlashcardsEmptyHint =>
'Use \"Add material → Create flashcards\" to write some, or \"⋮ → Import content\" to bring in an Anki deck or a list.';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetAddMaterial => 'Add material';
@override
String get studysetPathsInTopics => 'The paths are in its topics';
@override
String get studysetPathsInTopicsHint =>
'Or write one here: \"Add material → Create a path\".';
@override
String get studysetFlashcardsInTopics => 'The flashcards are in its topics';
@override
String get studysetFlashcardsInTopicsHint =>
'Or add some here: \"Add material → Create flashcards\".';
@override
String get studysetNotesInTopics => 'The notes are in its topics';
@override
String get studysetNotesInTopicsHint =>
'Or write one here: \"Add material → Create a note\".';
@override
String get studysetFilesInTopics => 'The files are in its topics';
@override
String get studysetFilesInTopicsHint =>
'Or add one here: \"Add material → Upload a PDF\".';
@override
String studysetBrowseAllFlashcards(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Browse all $count',
one: 'Browse it',
);
return '$_temp0';
}
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetCreateFlashcards => 'Create flashcards';
@override
String get studysetStudy => 'Study';
@override
String studysetLearnCount(int count) {
return 'Learn $count';
}
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String studysetLearnFlashcards(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Learn $count flashcards',
one: 'Learn 1 flashcard',
);
return '$_temp0';
}
@override
String get studysetNothingToStudy => 'Nothing to study yet';
@override
String get studysetStudyMode => 'Study mode';
@override
String studysetStartPath(String title) {
return 'Start: $title';
}
@override
String studysetContinuePath(String title) {
return 'Continue: $title';
}
@override
String get studysetStartPathHint => 'Begin the first step of this path.';
@override
String get studysetContinuePathHint =>
'Pick up the next path step where you left off.';
@override
String get studysetSpacedRepetition => 'Spaced repetition';
@override
String get studysetSpacedRepetitionHint =>
'Review the cards that are due, scheduled by FSRS.';
@override
String get studysetImportContent => 'Import content';
@override
String get studysetMenuExport => 'Export content';
@override
String get exportRunning => 'Exporting…';
@override
String get exportedMarkdown => 'Exported as Markdown.';
@override
String exportedZip(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Exported as Markdown with $count files (.zip).',
one: 'Exported as Markdown with 1 file (.zip).',
);
return '$_temp0';
}
@override
String exportMissingFiles(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count files weren\'t on this device and are left out.',
one: '1 file wasn\'t on this device and is left out.',
);
return '$_temp0';
}
@override
String get exportFailed => 'Couldn\'t export the studyset.';
@override
String get exportQuestion => 'Q';
@override
String get exportAnswer => 'A';
@override
String get exportHint => 'Hint';
@override
String get exportIfRight => 'If right';
@override
String get exportIfWrong => 'If wrong';
@override
String get exportSound => 'Sound';
@override
String get exportVideo => 'YouTube video';
@override
String get exportSpoiler => 'Spoiler';
@override
String get exportUnavailable => 'not available on this device';
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetImportPath => 'Learning paths';
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetImportPathHint =>
'From Halkyon path JSON files, one path each';
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetImportAnki => 'Anki deck';
@override
String get studysetImportAnkiHint => 'From an .apkg or .colpkg file';
@override
String get studysetImportCards => 'Flashcards';
@override
String get studysetImportCardsHint => 'Pasted, or from a CSV or TSV file';
@override
String get cardTextImportTitle => 'Import flashcards';
@override
String get cardTextImportDescription =>
'One card per line: its front, a separator, then its back. Each line is split at its first separator; lines without one are left out. A field in double quotes, as spreadsheets write them, may hold separators and line breaks.';
@override
String get cardTextImportHint => 'Paste the cards here';
@override
String get cardTextImportLoadFile => 'Load a CSV or TSV file';
@override
String get cardTextImportFileType => 'CSV or TSV files';
@override
String get cardTextImportUnreadable => 'This file doesn\'t hold text.';
@override
String get cardTextImportSeparator => 'Separator';
@override
String get cardTextSeparatorTab => 'Tab';
@override
String get cardTextSeparatorComma => 'Comma';
@override
String get cardTextSeparatorSemicolon => 'Semicolon';
@override
String get cardTextImportNone => 'No cards found yet.';
@override
String cardTextImportReady(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count cards ready to import.',
one: '1 card ready to import.',
);
return '$_temp0';
}
@override
String get cardTextImportSubmit => 'Import';
@override
String cardTextImported(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Imported $count flashcards.',
one: 'Imported 1 flashcard.',
);
return '$_temp0';
}
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetReorder => 'Reorder';
@override
String get studysetReorderPaths => 'Reorder paths';
@override
String get studysetReorderNotes => 'Reorder notes';
@override
String get studysetReorderFiles => 'Reorder files';
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetReorderDone => 'Done';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetOpenTopics => 'Show topics';
feat(studyset): the screens behind a studyset are its path Deeper studysets, on iOS and Android alike (decided 2026-10-11, after Material 3's and Apple's guidelines, Android's principles of navigation and Nielsen Norman Group's on breadcrumbs): - The screens behind a studyset are its path: below "Japanese › Grammar › Tae Kim's Grammar Guide" lie Grammar's screen, then Japanese's, then the library. A topic at any depth opens with the levels between pushed beneath it; the path's steps go up by popping to that level's page (they replaced the location before, so back could skip levels); a studyset opened from elsewhere gets its levels beneath it. Back, Android's predictive back, iOS's swipe from the edge and the path all lead one level up. - The path's steps are targets 48 dp high; a long press on back lists the levels above, the nearest first and the library last, as iOS's back buttons do. - Below expanded windows the topics open in a sheet from "3 topics ▾" at the end of the path's line, where Vaia puts "All topics"; no drawer, whose swipe from the edge is iOS's way back and which Material 3 Expressive deprecates. Topics with topics of their own unfold them, at any depth, in the sheet and the side column alike. - The navigation bar steps aside inside a studyset (its screen, notes, paths and dictionary): Material 3 puts it on the top-level screens and toolbars below, never both. Apple's guidelines keep the tab bar, lest people forget where they are; the path, starting at Library, says where. A strip as high as the system's bar keeps those screens clear of it. - The bar at the bottom keeps Add material and Study. A trial for review, on its own branch, on top of the floating navigation bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 13:03:27 +02:00
@override
String studysetTopicCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count topics',
one: '1 topic',
);
return '$_temp0';
}
@override
String get studysetShowInnerTopics => 'Show the topics inside';
@override
String get studysetHideInnerTopics => 'Hide the topics inside';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetTopics => 'Topics';
@override
String get studysetAllTopics => 'All topics';
@override
String get studysetNewSubStudyset => 'New sub-studyset';
@override
String get studysetSubStudysetsEmpty => 'No sub-studysets yet.';
@override
String get studysetSubStudysetsEmptyHint =>
'Break this studyset into milestones or lessons.';
@override
String get studysetDragToReorder => 'Drag to reorder';
@override
String get studysetMoveTitle => 'Move studyset to';
@override
String get studysetMoveTopLevel => 'Top level';
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get studysetMoveCurrent => '(current)';
@override
String get studysetMoveTopLevelRow => 'Top level (no parent)';
@override
String get studysetMoveArchived => 'Archived';
@override
String get studysetMoveNew => 'New studyset';
@override
String get studysetMoveSelectDestination => 'Select a destination';
@override
String studysetMoveDestination(String name) {
return 'Destination: $name';
}
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetMoveConfirm => 'Move';
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get studysetMoveShowInside => 'Show sub-studysets';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get studysetMoveHideInside => 'Hide sub-studysets';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String studysetMoved(String title, String destination) {
return 'Moved \"$title\" to $destination.';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
}
@override
String get studysetReadMore => 'Read more';
@override
String get studysetDescriptionTitle => 'Description';
@override
String get studysetDescriptionEditTitle => 'Edit description';
@override
feat(app): learning paths in the studyset screen: worked through and written - A Paths tab before Flashcards and Notes: each path with its title and secondary title, dimmed once completed; Edit, Reset progress (with an undo) and Delete (with an undo) behind its options. The studyset opens on its paths while one is unfinished, on its flashcards otherwise. - "Create a path" in Add material asks for the titles in a sheet and opens the new path to write; "Import a path (JSON)" reads a file in the import format, says what came in and how many blocks were left out (images and animations: the app never fetches from the web). - The player: one step at a time at reading width, a bar of the steps above, Back and "Check answers" / "Continue" / "Finish" below. It opens at the first unfinished step; an exercise passes only once answered right, as often as it takes; after a check, only the learner's own picks are marked, with the author's feedback; sources behind a chip; a finish screen leading to the next path. Words and items are dragged into place, or tapped into place for phones. - Writing: the path's screen (titles, description, tags, steps added by kind, reordered, deleted with an undo) and the step's (title, text with the format bar, its kind's exercise, feedback for right and wrong in the whole window, sources). The step saves on what it started from, as notes do, read-only while devices disagree until a version is kept. - The library and the topics count paths; steps done count on the dashboard, for the streak and the activity. - The rich-text screen for a studyset's description moves to core, for the steps' feedback too, and gains the format bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 09:25:08 +02:00
String get richTextSave => 'Save';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetSheetDescription => 'Description';
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get studysetSheetDescriptionAdd => 'Tap to add a description';
@override
String get studysetSheetDescriptionAdded => 'Description added — tap to edit';
@override
String get studysetSheetInheritColor => 'Inherit';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get studysetSheetLanguages => 'Languages';
@override
String get studysetSheetSetLanguages => 'Set languages';
@override
String get studysetSheetLanguagesHelper =>
'Which language each side of the cards is in. Chinese, Japanese and Korean then show in their own character shapes.';
@override
String get studysetSheetLanguagesInherited =>
'Off, or with neither side set, it takes the languages of the studyset it is in.';
@override
String get studysetSheetFrontLanguage => 'Front language';
@override
String get studysetSheetBackLanguage => 'Back language';
@override
String get studysetSheetLanguageNotSet => 'Not set';
@override
String get studysetSheetLicense => 'Licence';
@override
String get studysetSheetLicenseHelp => 'Help me choose';
@override
String get studysetSheetLicenseLabel => 'Licence';
@override
String get studysetSheetLicenseHelper =>
'The terms others may use the content under, shown below the studyset with a link to them. Examples: CC-BY-4.0, CC-BY-SA-4.0, MIT, All Rights Reserved.';
@override
String get studysetSheetLicenseYear => 'Year';
@override
String get studysetSheetLicenseYearHelper =>
'The copyright year, shown as “© 2019 Name”. Optional.';
@override
String studysetSheetLicenseYearInvalid(int min, int max) {
return 'Enter a year between $min and $max.';
}
@override
String get studysetSheetLicenseHolder => 'Licence holder';
@override
String get studysetSheetLicenseHolderHelper =>
'Who to credit: a name or an organisation.';
@override
String studysetSheetLicenseNewer(String license, String current) {
return '$license is an older Creative Commons licence. Its current version, $current, may fit better.';
}
@override
String get licenseChooserTitle => 'Choose a licence';
@override
String get licenseChooserSubtitle =>
'A few questions to find the Creative Commons licence that fits how you want your work shared.';
@override
String get licenseChooserIntroPrompt =>
'Do you want to keep any rights to your work?';
@override
String get licenseChooserIntroYes => 'Yes, share it under a licence';
@override
String get licenseChooserIntroYesDetail =>
'Others must follow the conditions you choose next.';
@override
String get licenseChooserIntroNo => 'No, dedicate it to the public domain';
@override
String get licenseChooserIntroNoDetail =>
'CC0: anyone may use it for anything, without conditions.';
@override
String get licenseChooserCommercialPrompt =>
'Allow others to use your work commercially?';
@override
String get licenseChooserYes => 'Yes';
@override
String get licenseChooserCommercialYesDetail => 'Including for profit.';
@override
String get licenseChooserNo => 'No';
@override
String get licenseChooserCommercialNoDetail =>
'Non-commercial uses only (NC).';
@override
String get licenseChooserAdaptationsPrompt =>
'Allow others to adapt or build upon your work?';
@override
String get licenseChooserAdaptationsYesDetail =>
'Remixes and adaptations are allowed.';
@override
String get licenseChooserAdaptationsShareAlike => 'Yes, if they share alike';
@override
String get licenseChooserAdaptationsShareAlikeDetail =>
'Adaptations carry this same licence (SA).';
@override
String get licenseChooserAdaptationsNoDetail =>
'Sharing only, no adaptations (ND).';
@override
String get licenseChooserBack => 'Back';
@override
String get licenseChooserRecommended => 'Recommended licence';
@override
String get licenseChooserUse => 'Use this licence';
@override
String get licenseSummaryCc0 =>
'Anyone may use it for anything, without conditions or credit.';
@override
String get licenseSummaryBy =>
'Others may share and adapt it, even commercially, as long as they credit you.';
@override
String get licenseSummaryBySa =>
'Others may share and adapt it, even commercially, if they credit you and license their adaptations the same way.';
@override
String get licenseSummaryByNc =>
'Others may share and adapt it for non-commercial purposes, with credit to you.';
@override
String get licenseSummaryByNcSa =>
'Others may share and adapt it non-commercially, with credit and the same licence on adaptations.';
@override
String get licenseSummaryByNd =>
'Others may share it, even commercially, with credit, but may not share adaptations.';
@override
String get licenseSummaryByNcNd =>
'Others may share it non-commercially, with credit, but may not adapt it: the strictest Creative Commons licence.';
@override
String get languageArabic => 'Arabic';
@override
String get languageGerman => 'German';
@override
String get languageGreek => 'Greek';
@override
String get languageEnglish => 'English';
@override
String get languageSpanish => 'Spanish';
@override
String get languageFrench => 'French';
@override
String get languageHebrew => 'Hebrew';
@override
String get languageHindi => 'Hindi';
@override
String get languageItalian => 'Italian';
@override
String get languageJapanese => 'Japanese';
@override
String get languageKorean => 'Korean';
@override
String get languageDutch => 'Dutch';
@override
String get languagePolish => 'Polish';
@override
String get languagePortuguese => 'Portuguese';
@override
String get languageRussian => 'Russian';
@override
String get languageSwedish => 'Swedish';
@override
String get languageThai => 'Thai';
@override
String get languageTurkish => 'Turkish';
@override
String get languageUkrainian => 'Ukrainian';
@override
String get languageVietnamese => 'Vietnamese';
@override
String get languageChineseSimplified => 'Chinese (Simplified)';
@override
String get languageChineseTraditional => 'Chinese (Traditional)';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get fieldRequired => 'required';
feat(app): the studyset screen with its topics, the tree's own screen A studyset's screen, as decided with the tree (2026-09-30/10-01): - A breadcrumb from the library down to the studyset; its steps go up (replacing the location, so back leads further up), and steps that don't fit fold into a "…" listing them, nearest first. - From expanded windows on, two columns: the description over the topics, the flashcards beside them, Study and Add material in the app bar. Below that, the topics are in a drawer, opened from a bar at the bottom that also holds Study and Add material and slides away while reading onwards; the back button stays where a drawer would have taken its place. - The topics: "All topics" for the whole studyset, then the studysets inside it in their order, each with the study it invites, what it holds and its options (edit, delete), opening on top of this screen; a switch rearranges them by dragging; "New sub-studyset" creates one inside. - Study covers the studyset with everything inside it ("Learn 12" while something is due). Add material offers flashcards for now. - Options: Edit (the sheet, now with the description, edited in the whole window, and for a studyset inside another the colour of the one it is in, which a new one takes at first), Move to… (the tree and the top level, without the studyset itself and what is inside it), Archive or Unarchive with Undo, Delete with Undo, which closes the screen. - The description cut short when long, the whole a tap away; the licence as the last line, linking to its text where that is known. The list's bloc, scoped to a parent, lists its children, works out the studyset's own progress, creates inside it, reorders and moves. The card list leaves adding and studying to the screen. The inline editing of the title and description, and its bloc, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 12:59:20 +02:00
@override
String get fieldRequiredHelper => 'Required';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get exploreTitle => 'Explore';
@override
String get exploreEmpty => 'Studysets that others share will appear here.';
@override
String get profileTitle => 'Profile';
@override
String get profileTabProfile => 'Profile';
@override
String get profileTabStatistics => 'Statistics';
@override
String get statisticsDays => 'Days';
@override
String get statisticsWeeks => 'Weeks';
@override
String get statisticsMonths => 'Months';
@override
String get statisticsTotal => 'In total';
@override
String get statisticsPerDay => 'Per day';
@override
String get statisticsRecord => 'Daily record';
@override
String get statisticsStudyTime => 'Study time';
@override
String get statisticsMinutesInfo =>
'In minutes, each card counted for at most a minute';
@override
String get statisticsHoursInfo =>
'In hours, each card counted for at most a minute';
@override
String get statisticsRatings => 'Ratings';
@override
String get statisticsRatingsInfo =>
'How you rated the flashcards you studied';
@override
String statisticsRated(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count cards rated',
one: '1 card rated',
zero: 'No cards rated',
);
return '$_temp0';
}
@override
String get statisticsToday => 'Today';
@override
String statisticsWeekOf(String date) {
return 'Week of $date';
}
@override
String get statisticsNothingYet =>
'Nothing studied yet. Your study time and ratings show here once you study flashcards.';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get profileSettingsTooltip => 'Settings';
@override
String get profileIdentified => 'Your progress is kept with your account.';
@override
String get profileAnonymous =>
'Progress is saved on this device. Sign in to keep it.';
@override
String get profileSignInWithGitHub => 'Sign in with GitHub';
@override
String get profileSignInFailed => 'Couldn\'t sign in with GitHub.';
@override
String get profileContinueInBrowser => 'Continue in your browser.';
@override
String get profileSwitchTitle => 'Switch to your GitHub account?';
@override
String get profileSwitchMessage =>
'This GitHub account already has progress from another device. The studysets on this device can\'t be combined with it yet: if you switch, they are deleted.';
@override
String get profileSwitchStay => 'Stay anonymous';
@override
String get profileSwitchConfirm => 'Switch';
feat: public profiles, a handle on the account domain with name, bio, links and picture Stage 1 of the public tier (ADR 0005). A person with an identity has a public profile: a handle by Mastodon's rules (1 to 30 letters, digits or underscores, unique whatever its case, a few kept for the instance), changeable until they first publish, on the instance's account domain (ACCOUNT_DOMAIN, else the server's host; the instance info names it), so `raisondetredev@halkyon.eu`; a display name, a bio, up to four links and an avatar. The table is server only: the app gets a Profile without the person's id. ProfileEndpoint (identified only) reads, saves and removes the caller's own; ProfilesEndpoint finds anyone's by handle. An avatar is a PNG of at most 1 MB that the app draws anew from the picture chosen, its middle square at 400 pixels, so a photo's metadata never leaves the device. The server keeps it in the public storage named by its content, so its URL tells nothing about whose it is, and deletes it once no profile shows it. Deleting a person deletes their profile and its picture. In the app, the Profile tab holds the profile as a form, laid out as the old app's; anonymous people are told GitHub sign-in gives them one. Migration 20261009184924904-public-profiles creates public_profile with a unique index on the person and one on the handle in lower case; it touches no other table, and the old server never sees it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
@override
String get publicProfileTitle => 'Public profile';
@override
String get publicProfileIntro =>
'How others see you once you publish: the handle they find you by, your name, a few words and links. It saves as you write, once you have chosen your handle.';
feat: public profiles, a handle on the account domain with name, bio, links and picture Stage 1 of the public tier (ADR 0005). A person with an identity has a public profile: a handle by Mastodon's rules (1 to 30 letters, digits or underscores, unique whatever its case, a few kept for the instance), changeable until they first publish, on the instance's account domain (ACCOUNT_DOMAIN, else the server's host; the instance info names it), so `raisondetredev@halkyon.eu`; a display name, a bio, up to four links and an avatar. The table is server only: the app gets a Profile without the person's id. ProfileEndpoint (identified only) reads, saves and removes the caller's own; ProfilesEndpoint finds anyone's by handle. An avatar is a PNG of at most 1 MB that the app draws anew from the picture chosen, its middle square at 400 pixels, so a photo's metadata never leaves the device. The server keeps it in the public storage named by its content, so its URL tells nothing about whose it is, and deletes it once no profile shows it. Deleting a person deletes their profile and its picture. In the app, the Profile tab holds the profile as a form, laid out as the old app's; anonymous people are told GitHub sign-in gives them one. Migration 20261009184924904-public-profiles creates public_profile with a unique index on the person and one on the handle in lower case; it touches no other table, and the old server never sees it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
@override
String get publicProfileAnonymousHint =>
'Sign in with GitHub to have a public profile and publish.';
@override
String get publicProfileDisplayName => 'Display name';
@override
String get publicProfileDisplayNameHint =>
'The name shown beside your handle';
@override
String get publicProfileHandle => 'Handle';
@override
String get publicProfileHandleHint =>
'Letters, digits and underscores, up to 30';
@override
String publicProfileAddress(String address) {
return 'Others find you as @$address';
}
@override
String get publicProfileHandleFixed =>
'Fixed since you published: others refer to it.';
@override
String get publicProfileBio => 'Bio';
@override
String get publicProfileLinks => 'Links';
@override
String get publicProfileLinksHint => 'Your site or other accounts, up to 4.';
@override
String get publicProfileLinkHint => 'https://…';
@override
String get publicProfileAddLink => 'Add link';
@override
String get publicProfileRemoveLink => 'Remove link';
@override
String get publicProfileSaving => 'Saving…';
@override
String get publicProfileSavedNow => 'Saved';
@override
String get publicProfileHandleChoose => 'Choose a handle';
@override
String get publicProfileHandleFirst =>
'Choose your handle first: your profile is made with it, and saves as you write from then on.';
@override
String get publicProfileHandleChooseTitle => 'Choose your handle';
@override
String get publicProfileHandleChangeTitle => 'Change your handle';
@override
String get publicProfileHandleDialogText =>
'Others find you by your handle. You can change it until you first publish a studyset.';
@override
String get publicProfileHandleChecking => 'Checking…';
@override
String publicProfileHandleFree(String handle) {
return '@$handle is free';
}
@override
String get publicProfileHandleYours => 'This is your handle';
@override
String get publicProfileHandleConfirmChoose => 'Choose';
@override
String get publicProfileHandleConfirmChange => 'Change';
feat: public profiles, a handle on the account domain with name, bio, links and picture Stage 1 of the public tier (ADR 0005). A person with an identity has a public profile: a handle by Mastodon's rules (1 to 30 letters, digits or underscores, unique whatever its case, a few kept for the instance), changeable until they first publish, on the instance's account domain (ACCOUNT_DOMAIN, else the server's host; the instance info names it), so `raisondetredev@halkyon.eu`; a display name, a bio, up to four links and an avatar. The table is server only: the app gets a Profile without the person's id. ProfileEndpoint (identified only) reads, saves and removes the caller's own; ProfilesEndpoint finds anyone's by handle. An avatar is a PNG of at most 1 MB that the app draws anew from the picture chosen, its middle square at 400 pixels, so a photo's metadata never leaves the device. The server keeps it in the public storage named by its content, so its URL tells nothing about whose it is, and deletes it once no profile shows it. Deleting a person deletes their profile and its picture. In the app, the Profile tab holds the profile as a form, laid out as the old app's; anonymous people are told GitHub sign-in gives them one. Migration 20261009184924904-public-profiles creates public_profile with a unique index on the person and one on the handle in lower case; it touches no other table, and the old server never sees it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
@override
String get publicProfileRemove => 'Remove profile';
@override
String get publicProfileRemoveTitle => 'Remove your public profile?';
@override
String get publicProfileRemoveMessage =>
'Your handle becomes free for others to take.';
@override
String get publicProfileRemoved => 'Profile removed.';
@override
String get publicProfileFailed => 'Couldn\'t reach the instance. Try again.';
@override
String get publicProfileUnreachable =>
'Your public profile can\'t be reached right now.';
@override
String get publicProfileRetry => 'Retry';
@override
String get profileProblemHandleInvalid =>
'A handle is 1 to 30 letters (a to z), digits or underscores.';
@override
String get profileProblemHandleReserved =>
'This handle is kept for the instance.';
@override
String get profileProblemHandleTaken => 'Someone else has this handle.';
@override
String get profileProblemPublished =>
'You have published: your handle can\'t change, nor your profile go.';
@override
String get profileProblemDisplayNameTooLong => 'At most 30 characters.';
@override
String get profileProblemBioTooLong => 'At most 500 characters.';
@override
String get profileProblemTooManyLinks => 'At most 4 links.';
@override
String get profileProblemLinkInvalid =>
'Links start with https:// or http://.';
@override
String get profileProblemAvatarInvalid =>
'This picture can\'t be used: choose a PNG, JPEG, WebP or GIF image.';
@override
String get publicProfilePicture => 'Picture';
@override
String get publicProfileChoosePicture => 'Choose picture';
@override
String get publicProfileRemovePicture => 'Remove picture';
@override
String get publicProfilePictureType => 'Images';
@override
String get publicProfilePictureLater =>
'Choose your handle to add a picture.';
feat: public profiles, a handle on the account domain with name, bio, links and picture Stage 1 of the public tier (ADR 0005). A person with an identity has a public profile: a handle by Mastodon's rules (1 to 30 letters, digits or underscores, unique whatever its case, a few kept for the instance), changeable until they first publish, on the instance's account domain (ACCOUNT_DOMAIN, else the server's host; the instance info names it), so `raisondetredev@halkyon.eu`; a display name, a bio, up to four links and an avatar. The table is server only: the app gets a Profile without the person's id. ProfileEndpoint (identified only) reads, saves and removes the caller's own; ProfilesEndpoint finds anyone's by handle. An avatar is a PNG of at most 1 MB that the app draws anew from the picture chosen, its middle square at 400 pixels, so a photo's metadata never leaves the device. The server keeps it in the public storage named by its content, so its URL tells nothing about whose it is, and deletes it once no profile shows it. Deleting a person deletes their profile and its picture. In the app, the Profile tab holds the profile as a form, laid out as the old app's; anonymous people are told GitHub sign-in gives them one. Migration 20261009184924904-public-profiles creates public_profile with a unique index on the person and one on the handle in lower case; it touches no other table, and the old server never sees it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 20:55:16 +02:00
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get settingsTitle => 'Settings';
@override
String get settingsAccount => 'Account';
@override
String get instanceWelcome => 'Welcome to Halkyon Learn';
@override
String get instanceFirstStartSubtitle =>
'Choose the instance to keep your studysets on. Someone runs each one; you can add others later.';
@override
String get instanceAddress => 'Instance address';
@override
String instanceUseBuiltIn(String name) {
return 'Use $name';
}
@override
String get instanceContinue => 'Continue';
@override
String get instanceAddressRequired => 'Enter the address of an instance.';
@override
String get instanceAddressInvalid => 'This isn\'t an address.';
@override
String instanceUnreachable(String name) {
return 'No instance of Halkyon Learn answers at $name.';
}
@override
String instanceOpenFailed(String name) {
return 'Couldn\'t open the app on $name.';
}
@override
String get settingsSwitchAccount => 'Switch account';
@override
String get accountsTitle => 'Accounts';
@override
String get accountAdd => 'Add account';
@override
String get accountAddSubtitle =>
'Choose the instance for the account to add.';
@override
String get accountRemoveTooltip => 'Remove account';
@override
String get accountRemoveTitle => 'Remove account?';
@override
String accountRemoveMessage(String name) {
return 'Removes the account on $name from this device, with all the device holds of it. What it synced stays on $name: sign in there with GitHub to have it again. An anonymous account can\'t be signed into again.';
}
@override
String get accountRemoveConfirm => 'Remove';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get settingsSignOut => 'Sign out';
@override
String get settingsSignOutTitle => 'Sign out?';
@override
String get settingsSignOutAnonymousMessage =>
'This account can\'t be signed into again, so signing out deletes it with its progress.';
@override
String get settingsDeleteAccount => 'Delete account';
@override
String get settingsDeleteTitle => 'Delete your account?';
@override
String get settingsDeleteMessage =>
'Everything you have, your studysets, cards and reviews, is deleted on the server and on all your devices. This can\'t be undone.';
@override
String get settingsAbout => 'About Halkyon Learn';
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
@override
String get aboutLegalese =>
'Free software: the app is under the GNU Affero General Public License, version 3 or any later version, the server under the Server Side Public License, version 1.';
@override
String get aboutSourceCode => 'The source of what this instance runs:';
@override
String get aboutSourceUnreachable =>
'Couldn\'t reach this instance to ask where its source is.';
@override
String get settingsAppearance => 'Appearance';
@override
String get settingsAppearanceDevice => 'Device theme';
@override
String get settingsAppearanceLight => 'Light';
@override
String get settingsAppearanceDark => 'Dark';
@override
String get settingsLanguage => 'Language';
@override
String get settingsLanguageDevice => 'Device language';
@override
String get settingsCitations => 'Citations';
@override
String get settingsCitationsDescription =>
'How sources and studysets\' bibliographies are cited on this device. Type authors\' full names: each style abbreviates and orders them itself.';
@override
String get settingsCitationStyle => 'Style';
@override
String get settingsCitationStyleApa => 'APA (7th edition)';
@override
String get settingsCitationStyleMla => 'MLA (9th edition)';
@override
String get settingsCitationStyleChicago => 'Chicago (18th edition)';
@override
String get settingsCitationPreview => 'Preview';
@override
String get settingsCitationGuide => 'Official guide';
@override
String get settingsStudy => 'Study';
@override
String get settingsStudyPomodoroTitle =>
'Show the Pomodoro timer while studying';
@override
String get settingsStudyPomodoroSubtitle =>
'Show the focus / break countdown inside the study session, so you can see the time left without opening the notification.';
@override
String settingsVersion(String version, String build) {
return 'Version $version (build $build)';
}
@override
String get syncTitle => 'Sync';
@override
String get syncStateSyncing => 'Syncing…';
@override
String get syncStateSynced => 'Everything synced';
@override
String get syncStateNotYet => 'Not synced yet';
@override
String get syncStateFailing => 'Sync is failing';
@override
String syncStateMismatch(String server) {
return 'This app can\'t sync with $server';
}
@override
String get syncMismatchDetail =>
'Update the app. If it is up to date, the server needs an update. What you do meanwhile stays on this device and syncs afterwards.';
@override
String syncLastCompleted(String time) {
return 'Last completed at $time';
}
@override
String get syncNoRunYet => 'No sync has completed since the app started';
@override
String syncLastFailed(String time) {
return 'Last attempt failed at $time';
}
@override
String get syncNow => 'Sync now';
@override
String syncPending(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count changes wait to sync',
one: '1 change waits to sync',
);
return '$_temp0';
}
@override
String get syncTroubleLabel => 'Sync needs attention';
@override
String syncMismatchBanner(String server) {
return 'This app can\'t sync with $server any more. Update it to keep syncing.';
}
@override
String get syncMismatchMore => 'More';
@override
String get tagsReadFailed => 'Couldn\'t read the tags.';
@override
String get notesReadFailed => 'Couldn\'t read the notes.';
@override
String get pathsReadFailed => 'Couldn\'t read the learning paths.';
@override
String get filesReadFailed => 'Couldn\'t read the files.';
@override
String get tagPickerTitle => 'Tags';
@override
String get tagPickerManage => 'Manage tags';
@override
String get tagPickerSearchLabel => 'Search or create a tag';
@override
String tagPickerCreate(String name) {
return 'Create \"$name\"';
}
@override
String get tagSheetCreateTitle => 'Create tag';
@override
String get tagSheetEditTitle => 'Edit tag';
@override
String get tagSheetNameLabel => 'Name';
@override
String get tagSheetNameHelper =>
'Capitals don\'t make another tag: \'Noun\' and \'noun\' are one.';
@override
String tagSheetNameExists(String name) {
return 'The tag \'$name\' exists already.';
}
@override
String tagSheetNameMerges(String name) {
return 'There is a tag \'$name\' already: saving merges this one into it.';
}
@override
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
String get tagSheetColor => 'Colour';
@override
String get tagSheetCreate => 'Create';
@override
String get tagSheetSave => 'Save';
@override
String get tagManageTitle => 'Manage tags';
@override
String get tagManageEmpty => 'No tags yet. Add one below.';
@override
String get tagManageAdd => 'Add tag';
@override
String tagManageRow(String name, int count) {
return '$name ($count)';
}
@override
String get tagManageEdit => 'Edit tag';
@override
String get tagManageHideJellyfin => 'Hide tags from Jellyfin while studying';
@override
String get tagManageHideJellyfinHint =>
'The shows, episodes, artists and songs words were met in. They stay on the cards, to filter and study by.';
@override
String get tagManageDelete => 'Delete tag';
@override
String tagDeleteTitle(String name) {
return 'Delete the tag \'$name\'?';
}
@override
String tagDeleteMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'It is removed from $count flashcards, on all your devices.',
one: 'It is removed from 1 flashcard, on all your devices.',
zero: 'It isn\'t on any flashcards.',
);
return '$_temp0';
}
@override
String get tagDeleteConfirm => 'Delete';
@override
String tagDeleted(String name) {
return 'Deleted the tag \'$name\'';
}
@override
String get studySettingsTitle => 'Session settings';
@override
String get studySettingsFilterByTags => 'Filter by tags';
@override
String get studySettingsNoTags =>
'No content tags yet. Add tags to your cards to filter them here.';
@override
String get studySettingsReset => 'Reset';
@override
String get studySettingsSave => 'Save';
@override
String get studySettingsHeadsUpTitle => 'Heads up!';
@override
String get studySettingsHeadsUpBody =>
'The changes you’ve made will reload the session, and you’ll lose your current progress.';
@override
String get studySettingsApplyChanges => 'Apply changes';
@override
String get studySettingsPerDay => ' / day';
@override
String studySettingsMoreTags(int count) {
return '+$count more';
}
@override
String get cardTagsLabel => 'Tags';
@override
String get cardTagsAdd => 'Add tags';
@override
String get cardTagsEdit => 'Edit tags';
@override
String tagChipsOverflow(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count tags',
one: '1 tag',
);
return '+$_temp0';
}
@override
String get cardHints => 'Hints';
@override
String cardHint(int number) {
return 'Hint $number';
}
@override
String get cardAddHint => 'Add hint';
@override
String get cardRemoveHint => 'Remove hint';
@override
String get cardExplanation => 'Explanation';
@override
String get cardAddExplanation => 'Add explanation';
@override
String get cardRemoveExplanation => 'Remove explanation';
@override
String cardHintsSheetTitle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Hints $count',
one: 'Hint',
);
return '$_temp0';
}
@override
String get cardPreviousHint => 'Previous hint';
@override
String get cardNextHint => 'Next hint';
@override
String get cardReport => 'Report';
@override
String get cardSenses => 'Senses';
@override
String get cardDictionaryEntry => 'Dictionary entry';
feat: a dictionary's corrections reach the cards made from it, by offer Every dictionary entry now has a version: 16 hex digits of the SHA-256 of what cards take of it (headword, reading, entry), made by the loader and carried on every hit. A card keeps the version it took (card schema 8). DictionaryEndpoint.changed returns, of the entries named with the versions cards keep (1,000 at most a call; the app asks 500 at a time), those whose version is another now. The card browser says how many cards are behind their dictionary and takes the corrections at a tap (decided 2026-10-09: by offer, never by themselves). Taking one writes the entry as it is now over the revision in which the card last took it, beside whatever came after, so the revisions merge per side as two devices' edits do (ADR 0003): a side only the dictionary changed takes the correction, a side the person changed too is a conflict settled in the composer, whose words now name that case. Cards made before versions are never offered one. The server rereads its dictionary folder hourly now, not daily: the dictionaries service fills its volume after an instance's first start, and an hour with nothing changed costs a hash of each file. Migration 20261009145434785-dictionary-entry-version: adds dictionary_entry.version, text NOT NULL DEFAULT '' (rows loaded before have none and are never reported as corrected), and by hand empties dictionary_source."fileHash", so every source loads once more and its entries get their versions; the schema stays as definition.sql says. The old server goes on working until the switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 17:32:00 +02:00
@override
String get cardDictionaryVersion => 'Dictionary version';
@override
String get studysetFromDictionary => 'From the dictionary';
@override
String get dictionaryTitle => 'Dictionary';
@override
String get dictionarySearchHint =>
'A word in kanji, kana or romaji, or a meaning';
feat(dictionary): German, Greek, English, Chinese and Korean words A studyset in German, Greek, English, Chinese or Korean finds its dictionary as one in Japanese does (ADR 0008), and its subtitles are cut into words by its language (`WordCutting`): Chinese by its longest words as they stand, the longer words over each cut offered as for Japanese; Korean at its spaces, a word the dictionary lacks as it stands taken without the particle it ends with (집에서 is 집, 아이 stays a child); every other language at its spaces, a word written exactly so first (the noun Essen before the verb essen). A studyset finds the dictionary of its language tag, else of its language alone (zh for zh-Hans). A Wiktionary word shows its genders, principal forms, IPA, romanisation and Hanja; a German or Greek noun of one gender goes onto its card with its article (das Haus, το σπίτι); its tag is its word class, named in full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would read as JMdict's codes. Articles, pronouns, prepositions and conjunctions are left out of a video's words. Chinese words show their pinyin over each character on the Watch screen, carry their HSK level as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and by none for a language without. A reading is typed in a session only when it is kana, so pinyin isn't. Words are looked up on the web on Jisho in Japanese, on Wiktionary at the language's section otherwise, and a search's hint speaks of kana and romaji only in Japanese. `DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance keeps (unset: de,el,en,ko,zh; empty: none). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
@override
String get dictionarySearchHintWords =>
'A word, any form of it, or a meaning';
@override
String dictionaryNothingFound(String query) {
return 'Nothing found for “$query”.';
}
@override
String get dictionaryUnreachable =>
'The dictionary can\'t be reached right now.';
@override
String get dictionaryRetry => 'Retry';
@override
String get dictionaryAddFailed => 'Couldn\'t add the words. Try again.';
@override
String get dictionaryMakingCards => 'Making the cards…';
@override
String get dictionaryNone =>
'This studyset\'s language has no dictionary on this instance.';
@override
String get dictionaryAdd => 'Add';
@override
String get dictionaryInStudyset => 'In this studyset';
@override
String dictionaryAdded(String word) {
return 'Added “$word”.';
}
@override
String get dictionaryLevels => 'Add a whole level';
@override
String get dictionaryLevelsHint =>
'Every word of a level becomes a flashcard, in the order of the level\'s list, tagged with its level and word class. Words already in this studyset are left out.';
@override
String get dictionaryLabelCommon => 'Common';
@override
String dictionaryAddLevel(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Add $count words',
one: 'Add 1 word',
);
return '$_temp0';
}
@override
String dictionaryAddingLevel(int done, int count) {
return 'Adding… $done of $count';
}
@override
String dictionaryWordsAdded(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Added $count words.',
one: 'Added 1 word.',
zero: 'No new words.',
);
return '$_temp0';
}
@override
String dictionaryWordsSkipped(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count were in this studyset already.',
one: '1 was in this studyset already.',
);
return '$_temp0';
}
@override
String dictionaryConfirmTitle(int count) {
return 'Add $count words?';
}
@override
String get dictionaryConfirmMessage =>
'Each becomes a flashcard in this studyset.';
@override
String get dictionaryConfirmAdd => 'Add';
@override
String dictionaryCredits(String sources) {
return 'Words from $sources.';
}
@override
String get dictionarySourcesTitle => 'Sources';
@override
String get dictionaryLookUp => 'Look up in the dictionary';
@override
String get dictionaryCharacterSearchHint =>
'A kanji or kana, a reading in kana or romaji, or a meaning';
@override
String get dictionaryTabWords => 'Words';
@override
String get dictionaryTabCharacters => 'Kanji & kana';
@override
String get dictionaryTabCharactersChinese => 'Hanzi';
@override
String get dictionaryTabCharactersKorean => 'Hangul & Hanja';
@override
String get dictionaryTabCharactersGreek => 'Alphabet';
@override
String get dictionaryCharacterSearchHintChinese =>
'A character, its pinyin, or a meaning';
@override
String get dictionaryCharacterSearchHintKorean =>
'A Hanja or Hangul letter, its sound, or a meaning';
@override
String get dictionaryCharacterSearchHintGreek => 'A letter or its name';
@override
String get dictionaryGroupHsk => 'HSK levels';
@override
String get dictionaryGroupCommonCharacters => 'In common use';
@override
String get dictionaryGroupHongKongGrades => 'Hong Kong school grades';
@override
String get dictionaryGroupHangul => 'Hangul';
@override
String get dictionaryGroupEducationHanja => 'Hanja taught in schools';
@override
String get dictionaryGroupGreek => 'Letters';
@override
String get dictionaryLabelHangul => 'Hangul letters';
@override
String get dictionaryLabelEducationHanja => '1,800 basic Hanja';
@override
String get dictionaryLabelGreek => 'Greek alphabet';
@override
String get dictionaryLabelCommonCharacters => '3,500 common characters';
@override
String get dictionaryLabelHiragana => 'Hiragana';
@override
String get dictionaryLabelKatakana => 'Katakana';
@override
String dictionaryGrade(int grade) {
return 'Grade $grade';
}
@override
String get dictionaryLabelSecondary => 'Secondary school';
@override
String get dictionaryGroupKana => 'Kana';
@override
String get dictionaryGroupJlpt => 'JLPT levels';
@override
String get dictionaryGroupGrades => 'School grades';
@override
String dictionaryStrokes(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count strokes',
one: '1 stroke',
);
return '$_temp0';
}
@override
String get dictionaryTabRadicals => 'Radicals';
@override
String get dictionaryRadicalsHint =>
'The 214 radicals that dictionaries order kanji by, grouped by their strokes. Choose one for the kanji written with it.';
@override
String get dictionaryAllRadicals => 'All radicals';
@override
String dictionaryRadicalNumber(int number) {
return 'Radical $number';
}
@override
String dictionaryRadicalKanjiCount(int count) {
return '$count kanji';
}
@override
String get dictionaryRadicalNone =>
'The instance\'s dictionary has no kanji under this radical yet.';
@override
String get dictionaryCharacterLevelsHint =>
'Every character of a level becomes a flashcard, in the order of the level\'s list, tagged with its level, school grade or script. Characters already in this studyset are left out.';
@override
String dictionaryAddCharacters(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Add $count characters',
one: 'Add 1 character',
);
return '$_temp0';
}
@override
String dictionaryCharactersAdded(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Added $count characters.',
one: 'Added 1 character.',
zero: 'No new characters.',
);
return '$_temp0';
}
@override
String dictionaryConfirmCharactersTitle(int count) {
return 'Add $count characters?';
}
@override
String dictionaryCharacterCredits(String sources) {
return 'Characters from $sources.';
}
feat: a dictionary's corrections reach the cards made from it, by offer Every dictionary entry now has a version: 16 hex digits of the SHA-256 of what cards take of it (headword, reading, entry), made by the loader and carried on every hit. A card keeps the version it took (card schema 8). DictionaryEndpoint.changed returns, of the entries named with the versions cards keep (1,000 at most a call; the app asks 500 at a time), those whose version is another now. The card browser says how many cards are behind their dictionary and takes the corrections at a tap (decided 2026-10-09: by offer, never by themselves). Taking one writes the entry as it is now over the revision in which the card last took it, beside whatever came after, so the revisions merge per side as two devices' edits do (ADR 0003): a side only the dictionary changed takes the correction, a side the person changed too is a conflict settled in the composer, whose words now name that case. Cards made before versions are never offered one. The server rereads its dictionary folder hourly now, not daily: the dictionaries service fills its volume after an instance's first start, and an hour with nothing changed costs a hash of each file. Migration 20261009145434785-dictionary-entry-version: adds dictionary_entry.version, text NOT NULL DEFAULT '' (rows loaded before have none and are never reported as corrected), and by hand empties dictionary_source."fileHash", so every source loads once more and its entries get their versions; the schema stays as definition.sql says. The old server goes on working until the switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-09 17:32:00 +02:00
@override
String dictionaryUpdatesBehind(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other:
'$count cards are behind the dictionary: their entries were corrected since they were made.',
one: '1 card is behind the dictionary: its entry was corrected since it was made.',
);
return '$_temp0';
}
@override
String get dictionaryUpdatesTake => 'Take the corrections';
@override
String dictionaryUpdatesTaken(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count cards updated.',
one: '1 card updated.',
);
return '$_temp0';
}
@override
String dictionaryUpdatesToSettle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count have a change of yours there too: open them to choose.',
one: 'One has a change of yours there too: open it to choose.',
);
return '$_temp0';
}
@override
String get dictionaryUpdatesFailed =>
'Couldn\'t take the corrections. Try again.';
@override
String get kanjiStrokeOrder => 'Stroke order';
@override
String get kanjiPlayAgain => 'Play again';
@override
String get kanjiNoStrokes => 'There\'s no stroke order for this character.';
@override
String get kanjiComponents => 'Components';
@override
String get studySettingsCharacters => 'Characters';
@override
String get studySettingsWriteByHand => 'Write by hand';
@override
String get studySettingsWriteByHandDescription =>
'Kanji, hanzi, Hanja, kana, Hangul and words written with them are written stroke by stroke and checked as you go, on a schedule of their own (a Korean word letter by letter). Words written aren\'t quizzed or typed.';
@override
String get writingHint => 'Hint';
@override
String get writingShow => 'Show it';
@override
String writingMistakes(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count mistakes',
one: '1 mistake',
zero: 'No mistakes',
);
return '$_temp0';
}
@override
String get writingWrongOrder => 'Not yet: that stroke comes later.';
@override
String get writingWrong => 'That isn\'t the next stroke.';
@override
String writingSuggested(String rating) {
return '$rating suggested';
}
@override
String get writingNoStrokes =>
'There\'s no stroke order for these characters: rate how well you knew them.';
@override
String get aboutDictionaries => 'Dictionaries on this instance';
@override
String get aboutAppData => 'Data in the app';
@override
String get cardReportTitle => 'Report an error';
@override
String get cardReportSubtitle =>
'Flag this card so you can find and fix it later.';
@override
String get cardReportTypo => 'Typo';
@override
String get cardReportGrammar => 'Grammar';
@override
String get cardReportContent => 'Content';
@override
String get cardReportOther => 'Other';
@override
String get cardReportNote => 'Note';
@override
String get cardReportNoteHint => 'What needs fixing?';
@override
String get cardReportSubmit => 'Report';
@override
String get cardReported => 'Flagged for fixing';
@override
String get cardResolveReport => 'Resolve report';
@override
String get cardReportResolved => 'Report resolved';
@override
String get cardBuriedBadge => 'Buried';
@override
String get cardUnbury => 'Unbury';
@override
String get cardTileBuried => 'Flashcard buried until tomorrow';
@override
String get cardTileUnburied => 'Flashcard unburied';
@override
String get cardDeleteTitle => 'Delete flashcard?';
@override
String get cardDeleteMessage =>
'This flashcard will be removed from the studyset. You can undo this for a few seconds.';
@override
String get cardDeleted => 'Flashcard deleted';
@override
String get cardFilterTooltip => 'Filter';
@override
String get cardFilterTitle => 'Filter';
@override
String get cardFilterTag => 'Tag';
@override
String get cardFilterAllTags => 'All tags';
@override
String get cardFilterNoTags => 'None of these flashcards has a tag yet.';
@override
String get cardFilterClear => 'Clear all';
@override
String get cardFilterApply => 'Apply';
@override
String get cardFilterNothing => 'No flashcards have this tag.';
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
@override
String get cardListFailed => 'Couldn\'t read the cards.';
@override
String get cardListMore => 'More';
@override
String get cardListManageTags => 'Manage tags';
@override
String get composerTitle => 'Create flashcards';
@override
String composerTitleFor(String title) {
return 'Create flashcards for \"$title\"';
}
Fix what the first Android run found The flow ran on a Pixel 9a and its emulator (2026-10-02); what it found: - The composer stayed on the card just written after "New flashcard", and on the original after "Duplicate": leaving a card changed the state before the new one was in it, so the screen took the new card for gone. It now waits for the card it asked for. - Bottom sheets reached under the status bar with the keyboard open (useSafeArea). - The field being written stayed behind the keyboard: the editor scrolls only within itself, so the card face now keeps it in view when it opens and when the keyboard rises, as a text field does. - A tap on read-only rich text put a caret in it and opened the keyboard on phones, and in the composer never opened the field. RichContentView is display only now: no selection, taps go to what surrounds it, a sound's button still plays (decided 2026-10-02). - The card types broke their names on a phone; they are choice chips that wrap (decided 2026-10-02). - The week overview marked days before the person's first activity as missed; they show nothing now. An import starts the activity too, without counting as studying (decided 2026-10-02). - A composer preview cut short gave no sign of it; it fades out over its last 32 dp, only when cut short (decided 2026-10-02). - "Isn't on this device yet" showed for any image or sound that failed; a file that is here but can't be decoded now says so, and offers no retry. - The signed-in account card was narrower than the anonymous one. - The composer's title said "Create flashcards" also when opened on a stored card; it says "Edit flashcards in …" then (decided 2026-10-02). - The Anki fixtures carry a valid PNG and a silent MP3, so they also serve to try the import by hand (decided 2026-10-02); regenerated with Anki 26.9.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 19:52:39 +02:00
@override
String get composerEditTitle => 'Edit flashcards';
@override
String composerEditTitleFor(String title) {
return 'Edit flashcards in \"$title\"';
}
@override
String get composerSaving => 'Saving…';
@override
String get composerSaveFailed => 'Save failed — retry';
@override
String get composerAllSaved => 'All changes saved';
@override
String get composerNewFlashcard => 'New flashcard';
@override
String get composerCollapseStack => 'Collapse stack';
@override
String get composerExpandStack => 'Expand stack';
@override
String get composerStatusSaving => 'Saving';
@override
String get composerStatusSaved => 'Saved';
@override
String get composerStatusFailed => 'Save failed';
@override
String get composerSaveFailedInline => 'Save failed.';
@override
String get composerRetry => 'Retry';
@override
String get composerEmptyPane => 'Add a flashcard to get started.';
@override
String get composerKindFlashcard => 'Flashcard';
@override
String get composerKindVocabulary => 'Vocabulary';
@override
String get composerKindMatching => 'Matching';
@override
String get composerKindMap => 'Map';
@override
String get composerKindCharacter => 'Character';
@override
String get composerLabelFront => 'Front';
@override
String get composerLabelQuestion => 'Question';
@override
String get composerLabelExpression => 'Expression';
@override
String get composerLabelPrompt => 'Prompt';
@override
String get composerLabelCharacter => 'Character';
@override
String get composerLabelBack => 'Back';
@override
String get composerLabelAnswers => 'Answers';
@override
String get composerLabelAnswer => 'Answer';
@override
String get composerPlaceholderFront => 'Add question';
@override
String get composerPlaceholderExpression => 'Add expression';
@override
String get composerPlaceholderPrompt => 'Add prompt';
@override
String get composerPlaceholderCharacter => 'Add a kanji or kana';
@override
String get composerAddAnswer => 'Add answer';
@override
String get composerCorrectAnswer => 'Correct answer';
@override
String get composerWrongAnswer => 'Wrong answer';
@override
String get composerRemoveAnswer => 'Remove answer';
@override
String composerPin(String field) {
return 'Pin $field: keep it for the next card';
}
@override
String composerUnpin(String field) {
return 'Unpin $field: clear it for the next card';
}
@override
String get composerTags => 'Tags';
@override
String get composerAddTags => 'Add tags';
@override
String get composerMoreActions => 'More actions';
@override
String get composerDuplicate => 'Duplicate';
@override
String get composerClearFields => 'Clear fields';
@override
String get composerDelete => 'Delete';
@override
String get composerDeleteTitle => 'Delete this flashcard?';
@override
String get composerDeleteDraftMessage =>
'The draft will be removed from this session. It has not been saved.';
@override
String get composerDeleteSavedMessage =>
'The flashcard will be removed from the studyset, on all your devices.';
@override
String composerDiscardTitle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Discard unsaved flashcards?',
one: 'Discard unsaved flashcard?',
);
return '$_temp0';
}
@override
String composerDiscardMessage(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other:
'$count flashcards haven\'t been saved yet. Leaving now will discard them.',
one: '1 flashcard hasn\'t been saved yet. Leaving now will discard it.',
);
return '$_temp0';
}
@override
String get composerDiscardConfirm => 'Discard';
@override
String get composerConflict =>
'This flashcard was changed in two ways at once: on two of your devices, or by you and by a correction from the dictionary or a new release of what it was copied from. Choose the version to keep of each part below.';
@override
String get composerKeepThis => 'Keep this';
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
@override
String get formatAlignLeft => 'Align left';
@override
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
String get formatAlignCenter => 'Centre';
feat(app): notes in the studyset screen, written in the whole window - A Notes tab beside Flashcards lists a studyset's notes in order: the title ("Untitled note" without one) and when each was last edited ("Edited 5 minutes ago", by the calendar from yesterday on, the date after a week); Edit and Delete behind its options. Deleting asks, then offers Undo for a moment (`NoteDeletions`). With none, the tab says so. - "Create a note" in Add material opens a new note over the tabs; so does a note of the list. The note: a title, its tags once it exists, and its text at reading width; the app bar says how saving stands. - `NoteEditorBloc` saves after 5 seconds without typing, when the app goes to the background, and when the note is closed (the bloc saves as it closes, so leaving never waits, and the write outlives the screen). Each save builds on the revisions the editor started from; another device's change takes over a note with nothing unsaved, and merges with one that has. A note changed differently on two devices is read-only until the person keeps a version of each field in conflict. A new note is created by its first save, never when left blank; a note deleted elsewhere says it no longer exists. - The editor gains a format bar, docked below it while it has the focus (`RichContentEditor(formatBar: true)`): alignment, bold, italic, underline, superscript and subscript. It takes no focus, so the selection stays. Superscript and subscript are inline marks of their own, drawn by the font's raised and lowered glyphs in editors and read-only text alike. - The library's and the topics' counts include notes (a studyset with what is inside it, as for cards). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 22:30:53 +02:00
@override
String get formatAlignRight => 'Align right';
@override
String get formatBold => 'Bold';
@override
String get formatItalic => 'Italic';
@override
String get formatUnderline => 'Underline';
@override
String get formatSuperscript => 'Superscript';
@override
String get formatSubscript => 'Subscript';
@override
String get formatRuby => 'Text above (a pronunciation, a translation)';
@override
String get formatRubyFurigana => 'Furigana (a reading above the text)';
@override
String get formatRubyPinyin => 'Pinyin (above the characters)';
@override
String get formatLink => 'Link';
@override
String get linkTitle => 'Link';
@override
String get linkText => 'Text';
@override
String get linkAddress => 'Address';
@override
String get linkAddressHint => 'https://…';
@override
String get linkSave => 'Save';
@override
String get linkRemove => 'Remove link';
@override
String get rubyTitle => 'Text above';
@override
String get rubyTitleFurigana => 'Furigana';
@override
String get rubyTitlePinyin => 'Pinyin';
@override
String get rubyText => 'Text';
@override
String get rubyHint => 'e.g. a pronunciation or a translation';
@override
String get rubyReading => 'Reading';
@override
String get rubyReadingHint => 'In kana, e.g. かんじ';
@override
String get rubyHintPinyin => 'With tone marks, e.g. hànzì';
@override
String get rubySave => 'Save';
@override
String get rubyRemove => 'Remove';
@override
String get vocabularyReading => 'Reading';
@override
String get vocabularyReadingHint => 'Kana reading';
@override
String get vocabularyMeanings => 'Meanings';
@override
String get vocabularyPrimaryMeaningHint => 'Primary meaning';
@override
String get vocabularyMeaningHint => 'Meaning';
@override
String get vocabularyAddMeaning => 'Add meaning';
@override
String get vocabularyRemoveMeaning => 'Remove meaning';
@override
String get vocabularyGlossHint => 'Translation';
@override
String get vocabularyAddGloss => 'Add translation';
@override
String get vocabularyRemoveGloss => 'Remove translation';
@override
String get vocabularyAddSense => 'Add sense';
@override
String get vocabularyRemoveSense => 'Remove sense';
@override
String get characterPrimaryMeaningHint =>
'Primary meaning, or a kana\'s romaji';
@override
String get characterOnReadings => 'On readings';
@override
String get characterOnReadingsHint => 'From Chinese, in katakana: ショク、ジキ';
@override
String get characterKunReadings => 'Kun readings';
@override
String get characterKunReadingsHint =>
'Japanese, in hiragana, okurigana after a dot: た.べる';
@override
String get characterOn => 'On';
@override
String get characterKun => 'Kun';
@override
String get characterPinyin => 'Pinyin';
@override
String get characterEum => 'Eum';
@override
String get characterHun => 'Hun';
@override
String get characterPinyinReadings => 'Pinyin';
@override
String get characterPinyinReadingsHint =>
'With tone marks or numbers: shuǐ, shui3';
@override
String get characterEumReadings => 'Sounds (eum)';
@override
String get characterEumReadingsHint => 'Sino-Korean, in Hangul: 수';
@override
String get characterHunReadings => 'Native meanings (hun)';
@override
String get characterHunReadingsHint =>
'Korean words for its meaning, in Hangul: 물';
@override
String get characterPrimaryMeaningHintLetter =>
'Primary meaning, or a letter\'s sound';
@override
String get composerPlaceholderCharacterChinese => 'Add a character';
@override
String get composerPlaceholderCharacterKorean =>
'Add a Hanja or Hangul letter';
@override
String get composerPlaceholderCharacterGreek => 'Add a letter';
@override
String get vocabularyReadingHintPinyin =>
'Pinyin, with tone marks or numbers';
@override
String get vocabularyReadingHintPronunciation =>
'Pronunciation, where it differs from the spelling';
@override
String get matchingCategories => 'Categories';
@override
String get matchingCategoriesHint =>
'The drop-zone cards learners drag items onto. Each category needs at least one item, so a card can\'t have more categories than items.';
@override
String matchingCategory(int number) {
return 'Category $number';
}
@override
String get matchingAddCategory => 'Add category';
@override
String get matchingAddCategoryBlocked =>
'Add an item first: a card can\'t have more categories than items';
@override
String get matchingRemoveCategory => 'Remove category';
@override
String get matchingMinCategories => 'At least two categories required';
@override
String get matchingItems => 'Items';
@override
String get matchingItemsHint =>
'The draggable items. Assign every item to its correct category: an unassigned item can never be placed right.';
@override
String matchingItem(int number) {
return 'Item $number';
}
@override
String get matchingAddItem => 'Add item';
@override
String get matchingUnassigned => 'Unassigned';
@override
String get matchingRemoveItem => 'Remove item';
@override
String get matchingMinItems => 'At least two items required';
@override
String get cardTileOptions => 'Options';
@override
String get cardTileEdit => 'Edit';
@override
String get cardTileDelete => 'Delete';
@override
String get cardTileUntitled => 'Untitled';
@override
String get cardTileConflict => 'Changed on two devices';
@override
String get studyClose => 'Close session';
@override
String get studyReviewsDueTitle => 'You have reviews due';
@override
String studyReviewsDueBody(int reviews, int news) {
String _temp0 = intl.Intl.pluralLogic(
reviews,
locale: localeName,
other: '$reviews cards are',
one: '1 card is',
);
String _temp1 = intl.Intl.pluralLogic(
news,
locale: localeName,
other: '$news new cards',
one: '1 new card',
);
return '$_temp0 due for review. Learning $_temp1 now will add to your review load.';
}
@override
String get studyReviewsDueAllTagsNote =>
'“Review first” studies the whole backlog across every tag — your tag filter doesn\'t apply until it\'s clear.';
@override
String get studyReviewFirst => 'Review first';
@override
String get studyLearnAnyway => 'Learn anyway';
@override
String get studySettingsTagFilterPausedForBacklog =>
'This session is clearing your review backlog across every tag, so these tags aren\'t narrowing it. They apply again to your next session.';
@override
String get studyBuryCard => 'Bury until tomorrow';
@override
String get studyCardBuried => 'Card buried until tomorrow';
@override
String get studyUndo => 'Undo';
@override
String get studyLockScreen => 'Lock screen';
@override
String get studyUnlock => 'Unlock';
@override
String get studyTapToUnlock => 'Tap to unlock';
@override
String get studyLoadFailed => 'Couldn\'t read this session.';
@override
String get studyRatingFailed =>
'Couldn\'t keep your rating. Rate the card again.';
@override
String get studyRetry => 'Retry';
@override
String get studyScopeTitle => 'Study scope';
@override
String get studyScopeDescription =>
'This studyset has sub-studysets. Pick which cards to review.';
@override
String get studyScopeEverythingTitle => 'Everything';
@override
String get studyScopeEverythingSubtitle =>
'This studyset and all sub-studysets';
@override
String get studyScopeSelfTitle => 'This studyset only';
@override
String get studyScopeSelfSubtitle => 'Skip cards in sub-studysets';
@override
String get studyBackFirstOn =>
'Showing the back of cards first: tap to revert';
@override
String get studyBackFirstOff => 'Show the back of cards first';
@override
String get studyShowAnswer => 'Show answer';
@override
String get studyCheck => 'Check';
@override
String get studySelectAll => 'Select all that apply';
@override
String get studySelectOne => 'Select the correct answer';
@override
String get studyMatchingInstructions => 'DRAG AND DROP ONTO CARDS ABOVE';
@override
String get studyMatchingAllPlaced => 'All placed';
@override
String get studyMatchingDropHere => 'Drop here';
@override
String get studyMapTapPrompt => 'Tap a region to answer';
@override
String studyMapSelected(String label) {
return 'Selected: $label';
}
@override
String get studyCorrect => 'Correct';
@override
String studyCorrectLabel(String label) {
return 'Correct — $label';
}
@override
String studyMapAnswer(String answer) {
return 'Answer: $answer';
}
@override
String studyMapPickedAnswer(String picked, String answer) {
return 'You picked $picked · Answer: $answer';
}
@override
String get studyRatingBad => 'Bad';
@override
String get studyRatingOk => 'OK';
@override
String get studyRatingGood => 'Good';
@override
String get studyRatingPerfect => 'Perfect';
@override
String get studyNothingTitle => 'Nothing to study right now';
@override
String get studyNothingBody => 'Come back later when more cards are due.';
@override
String studyNextDue(String interval) {
return 'The next card is due in $interval.';
}
@override
String get studyDone => 'Done';
@override
String get studySessionComplete => 'Session complete';
@override
String studyProgressReviewed(int reviewed, int total) {
return '$reviewed / $total reviewed';
}
@override
String studyReviewedCount(int reviewed) {
return '$reviewed reviewed';
}
@override
String get studyStatCards => 'Cards';
@override
String get studyStatAccuracy => 'Accuracy';
@override
String get studyStatTime => 'Time';
@override
String studyDurationSec(int sec) {
return '$sec sec';
}
@override
String studyDurationMin(int min) {
return '$min min';
}
@override
String studyDurationMinSec(int min, int sec) {
return '$min min $sec sec';
}
@override
String get studyLeaveTitle => 'Leave this study session?';
@override
String studyLeaveRated(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other:
'Your ratings so far are saved, but the $count cards still left in this round will be discarded if you leave now.',
one: 'Your ratings so far are saved, but the last card in this round will be discarded if you leave now.',
);
return '$_temp0';
}
@override
String studyLeaveUnrated(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other:
'You haven’t rated any of the $count cards in this round yet — leaving ends the session without recording any progress for it.',
one: 'You haven’t rated this card yet — leaving ends the session without recording any progress for this round.',
);
return '$_temp0';
}
@override
String get studyLeaveConfirm => 'Leave';
@override
String get studyKeepStudying => 'Keep studying';
@override
String studyInMinutes(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'in $count min',
one: 'in 1 min',
);
return '$_temp0';
}
@override
String studyInHours(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'in $count hours',
one: 'in 1 hour',
);
return '$_temp0';
}
@override
String studyInDays(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'in $count days',
one: 'in 1 day',
);
return '$_temp0';
}
@override
String studyInMonths(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'in $count months',
one: 'in 1 month',
);
return '$_temp0';
}
@override
String studyInYears(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'in $count years',
one: 'in 1 year',
);
return '$_temp0';
}
@override
String get studyContinue => 'Continue learning';
@override
String get studyPrevious => 'Previous card';
@override
String get studySkipCard => 'Skip card';
@override
String studyMinutes(int count) {
return '$count min';
}
@override
String studyHours(int count) {
return '$count h';
}
@override
String studyDays(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count days',
one: '1 day',
);
return '$_temp0';
}
@override
String studyMonths(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count months',
one: '1 month',
);
return '$_temp0';
}
@override
String studyYears(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count years',
one: '1 year',
);
return '$_temp0';
}
@override
String studyFilteredBy(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Narrowed to $count tags',
one: 'Narrowed to 1 tag',
);
return '$_temp0';
}
@override
String get studySettingsCardsPerRound => 'Cards per round';
@override
String get studySettingsNewPerDay => 'New cards per day';
@override
String get studySettingsNewPerDayHint =>
'How many never-studied cards each studyset starts a day, unless its own study options say otherwise.';
@override
String get studyOptionsTitle => 'Study options';
@override
String get studyOptionsDescription =>
'How many cards this studyset brings a day, and which come first, on every device. The studysets inside it keep their own.';
@override
String get studyOptionsReviewsPerDay => 'Reviews per day';
@override
String get studyOptionsOrder => 'Order';
@override
String get studyOptionsReviewsFirst => 'Reviews first';
@override
String get studyOptionsMixed => 'Mixed';
@override
String get studyOptionsNewFirst => 'New first';
@override
String get studyOptionsReset => 'Reset to defaults';
@override
String get studySettingsUnlimited => 'Unlimited';
@override
String get studySettingsSortBy => 'Sort by';
@override
String get studySettingsLearnMode => 'Learn mode';
@override
String get studyModeSpaced => 'Spaced repetition';
@override
String get studyModeSpacedHint =>
'Due cards on their schedules; ratings move them.';
@override
String get studyModeCram => 'Cram';
@override
String get studyModeCramHint =>
'Walk every card without touching the schedules.';
@override
String get studySettingsVocabulary => 'Vocabulary';
@override
String get studySettingsQuizMc => 'Quiz with multiple choice';
@override
String get studySettingsQuizMcDescription =>
'Show vocabulary cards as a multiple-choice question, with wrong answers drawn from other vocabulary in this set.';
@override
String get studySettingsTypeReading => 'Type the reading';
@override
String get studySettingsTypeReadingDescription =>
'Words with kanji ask for their reading, typed in kana (romaji turn into kana as you type); Chinese words for their pinyin (tone numbers turn into marks: ni3 becomes nǐ).';
@override
String get studyTypedHint => 'The reading, in kana or romaji';
@override
String get studyTypedHintPinyin => 'The pinyin, with tone numbers or marks';
@override
String get studyTypedRight => 'Right';
@override
String studyTypedWrong(String answer) {
return 'You typed $answer';
}
@override
String get studyAspectMeaning => 'Meaning';
@override
String get studyAspectReading => 'Reading';
@override
String get studyAspectExpression => 'Expression';
@override
String get studyAspectMixed => 'Mixed';
@override
String get studySettingsAspectDescription =>
'What to recall — the meaning, the reading (kana, pinyin), or the expression itself (shown the meaning). Mixed varies meaning and reading per card; cards that can’t be quizzed stay a normal flip.';
@override
String get studySortOldestFirst => 'Oldest first';
@override
String get studySortRandom => 'Random';
@override
String get studySortNewestFirst => 'Newest first';
@override
String get studyFewer => 'Decrease';
@override
String get studyMore => 'Increase';
@override
String get countStepperType => 'Type a number';
@override
String get studyLimitNewPerDay => 'Limit new cards per day';
@override
String get studyLimitReviewsPerDay => 'Limit reviews per day';
@override
String get studysetMenuReset => 'Reset progress';
@override
String get studysetResetTitle => 'Reset progress?';
@override
String get studysetResetMessage =>
'What you choose starts afresh, in it and in the studysets inside it, on all your devices. Your study history stays. You can undo this for a few seconds.';
@override
String get studysetResetCards => 'Flashcards';
@override
String studysetResetCardsDetail(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards start again as new',
one: '1 flashcard starts again as new',
);
return '$_temp0';
}
@override
String get studysetResetPdfs => 'Reading of PDFs';
@override
String studysetResetPdfsDetail(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count PDFs are read from their first page again',
one: '1 PDF is read from its first page again',
);
return '$_temp0';
}
@override
String get studysetResetPaths => 'Learning paths';
@override
String studysetResetPathsDetail(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count learning paths start from their first step again',
one: '1 learning path starts from its first step again',
);
return '$_temp0';
}
@override
String get studysetResetNothing => 'Nothing in it has progress yet.';
@override
String get studysetResetConfirm => 'Reset';
@override
String get studysetResetDone => 'Progress reset';
@override
String get mapUnknown => 'This map needs a newer version of the app.';
@override
String get mapPickRegion =>
'Choose a part of the map, then tap the region that answers the question. Tiny places have a ring; where rings crowd, zoom in.';
@override
String get mapNonePicked => 'No region picked yet';
@override
String get mapPickTitle => 'Pick the region';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
@override
String get mapNameJapan => 'Japan';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
@override
String get mapNameWorld => 'World';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
@override
String get mapPrefectures => 'Prefectures';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
@override
String get mapZoomNote => 'Ctrl + scroll to zoom';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
@override
String get mapZoomNoteMac => '⌘ + scroll to zoom';
feat(map): every country on earth, picked continent first Map questions can now be on the world's countries besides Japan's prefectures: a new question chooses its map above it. The world is Natural Earth's 1:50m countries (public domain), as they are on the ground, one region per ISO 3166-1 code (239; Gibraltar and the US Outlying Islands from the 1:10m layer, which has them), named in every language a studyset can be in from the Unicode CLDR (Unicode License v3, its notice in the licence list). tool/maps/world_countries.py builds it; the outline code it shares with the Japan script moved to outlines.py, which still writes Japan's file byte for byte. The 1.8 MB file is parsed off the screen's isolate. Picking goes in layers on both maps: on the whole map one taps a group (a continent, or Kantō, Kansai …; chips name them too) and the map flies there, then one taps the region; Back returns. A group of one (Hokkaidō, Okinawa) and what is in none (Antarctica) are picked at once. A country in two groups (Russia, Türkiye, Cyprus) opens the one nearer the tap. Oceania's frame runs past 180° so the Pacific stays in one piece. What is still too small to hit gets a ring, 48 dp of reach as Material's touch target: on the ring it is picked; near it, where the tap could mean it or its neighbours, the map zooms in rather than guess. Pinching and a mouse's wheel zoom as before. Names show in the app's language, beside them the map's own (Japanese for Japan) or the studyset's (キプロス beside Cyprus in a Japanese studyset). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-12 07:18:20 +02:00
@override
String get mapGroupHokkaido => 'Hokkaidō';
@override
String get mapGroupTohoku => 'Tōhoku';
@override
String get mapGroupKanto => 'Kantō';
@override
String get mapGroupChubu => 'Chūbu';
@override
String get mapGroupKansai => 'Kansai';
@override
String get mapGroupChugoku => 'Chūgoku';
@override
String get mapGroupShikoku => 'Shikoku';
@override
String get mapGroupKyushu => 'Kyūshū';
@override
String get mapGroupOkinawa => 'Okinawa';
@override
String get mapGroupEurope => 'Europe';
@override
String get mapGroupAfrica => 'Africa';
@override
String get mapGroupWestAsia => 'Western Asia';
@override
String get mapGroupSouthAsia => 'Central and South Asia';
@override
String get mapGroupEastAsia => 'East Asia';
@override
String get mapGroupSoutheastAsia => 'Southeast Asia';
@override
String get mapGroupNorthAmerica => 'North America';
@override
String get mapGroupCentralAmerica => 'Central America and the Caribbean';
@override
String get mapGroupSouthAmerica => 'South America';
@override
String get mapGroupOceania => 'Oceania';
@override
String get composerPlaceholderQuestion => 'Add question';
@override
String get composerLabelRegion => 'Answer';
@override
String get composerRegionPick => 'Tap to pick the region on the map';
@override
String get homeStreakTooltip => 'Study streak';
@override
String get homeArrange => 'Arrange widgets';
@override
String get homeDone => 'Done';
@override
String get homeDragToReorder => 'Drag to reorder';
@override
String homeShowWidget(String label) {
return 'Show $label';
}
@override
String homeHideWidget(String label) {
return 'Hide $label';
}
@override
String get homeAllHidden => 'All widgets are hidden';
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get homeAllHiddenBody => 'Bring some back to fill your dashboard.';
@override
String get homeCustomise => 'Customise';
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get homeWidgetGoal => 'Daily goal';
@override
String get homeWidgetWeek => 'Week overview';
@override
String get homeWidgetActivity => 'Activity heatmap';
@override
String get homeActivityTitle => 'Your activity';
@override
String streakDays(num count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count day streak',
one: '1 day streak',
);
return '$_temp0';
}
@override
String get streakStart => 'Study today to start your streak.';
@override
String get streakGrow =>
'Keep it going: study at least once a day to grow your streak.';
@override
String get streakFirstDay =>
'Review some cards today to light the first day.';
@override
String get streakComeBack => 'Come back tomorrow to keep your streak.';
@override
String get streakKeepAlive => 'Study today to keep your streak alive.';
@override
String get goalEditTitle => 'Edit study goal';
@override
String get goalSetTitle => 'Set a study goal';
@override
String get goalDescriptionDaily =>
'Review a number of flashcards each day. Every card you rate counts.';
@override
String get goalDescriptionWeekly =>
'Review a number of flashcards each week, from Monday. Every card you rate counts.';
@override
String get goalPeriod => 'Period';
@override
String get goalPerDay => 'Per day';
@override
String get goalPerWeek => 'Per week';
@override
String get goalCardsPerDay => 'Cards per day';
@override
String get goalCardsPerWeek => 'Cards per week';
@override
String get goalSave => 'Save goal';
@override
String get goalRemove => 'Remove goal';
@override
String get goalEmptySubtitle =>
'Review a set number of cards each day or week.';
@override
String get goalDailyHeadline => 'Daily goal';
@override
String get goalWeeklyHeadline => 'Weekly goal';
@override
String goalProgressToday(int reviewed, int target) {
return '$reviewed of $target cards today';
}
@override
String goalProgressThisWeek(int reviewed, int target) {
return '$reviewed of $target cards this week';
}
@override
String get goalMet => 'Goal met: nice work!';
@override
String goalRemaining(int count) {
return '$count to go';
}
@override
String get weekToday => 'Today';
@override
String get weekPreviousDay => 'Previous day';
@override
String get weekNextDay => 'Next day';
@override
String get weekRestDay => 'Rest day';
@override
String get heatmapLess => 'Less';
@override
String get heatmapMore => 'More';
@override
String heatmapTotal(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count activities in the last year',
one: '1 activity in the last year',
);
return '$_temp0';
}
@override
String heatmapDay(int count, String date) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count activities',
one: '1 activity',
);
return '$_temp0 · $date';
}
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get commonCancel => 'Cancel';
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button Blocks beyond the editor's own, each built once for editor and view: - callout: eight kinds in theme colours, kind, title and "folded at first" from its header; written in place, left with Enter on its empty last line; never inside another - spoiler: written in plain sight, hidden behind Reveal read-only - formula on its own line, written on its own screen with a preview and snippet buttons, and formulas within a line (Σ in the format bar); set by flutter_math_fork behind MathView, LaTeX's numbered environments read as their aligned forms - image and sound picked from the device, kept as synced files - YouTube video from its link, with its start and a caption: a card until played, nothing fetched before; played in place where a web view exists (youtube_player_iframe, privacy-enhanced), else opened in the browser, behind YoutubeVideos in Dependencies - study button: a studyset, a tag or all cards, with or without its topics, back first; its StudyFocus rides on the study route and narrows that session only - tables, already in the editor, now insertable everywhere An Insert button opens on the format bar, so blocks can be inserted on phones too; the desktop's / menu lists the same blocks, translated. Card faces now dock the format bar while written, so cards get blocks and formatting on phones as well. The blocks are listed in EditorBlocks: the core's, plus the features' through EditorBlocksScope. Blocks of a type the app doesn't know say they need a newer version and stay as they are, so formatVersion stays 1. Their types and keys are in content_blocks.dart, which RichContent reads: a block's text counts as text, and a video or study button isn't blank. The path import now makes formulas real ones. integration_test/youtube_player_test.dart plays a real video through the app's player on a device, which no widget test can: YouTube refuses embeds that don't say where they are embedded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 12:20:41 +02:00
@override
String get commonSave => 'Save';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get commonClose => 'Close';
@override
String get commonUndo => 'Undo';
@override
String get moreInfo => 'More info';
@override
String get moreInfoCreated => 'Created';
@override
String get moreInfoModified => 'Last modified';
@override
String get moreInfoItemId => 'Item ID';
@override
String get moreInfoStudysetId => 'Studyset ID';
@override
String get moreInfoPdfId => 'PDF source ID';
@override
String get moreInfoParentId => 'Parent ID';
@override
String get moreInfoFile => 'File';
@override
String get moreInfoReviews => 'Reviews';
@override
String get moreInfoReviewCount => 'Review count';
@override
String get moreInfoLastReviewed => 'Last reviewed';
@override
String get moreInfoCopy => 'Copy';
@override
String get copiedToClipboard => 'Copied to clipboard';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get commonTryAgain => 'Try again';
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
@override
String get personDataFailed => 'Couldn\'t open this device\'s data.';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
@override
String get sessionUnreachable => 'Couldn\'t reach the server.';
@override
String get notFoundGone =>
'This no longer exists. It may have been deleted on another device.';
Import Anki decks with their files and history Files in content: the person's images and sounds as asset rows synced like all content, their files kept by hash on each device, uploaded after each sync, downloaded on demand and checked against their hash (core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in the editor's image block and an audio block of ours; any other URL is never fetched. Sounds play through audioplayers, one at a time. Content holding only an image or a sound isn't blank, and lists show it as such. The Anki import ("Import from Anki" under Add material): .apkg and .colpkg packages in all three formats Anki has written, read on an isolate of their own. Modern packages compress everything with zstd in frames that don't record their size, which pure_zstd v1.0.0 refuses; our fork decodes them. Fields become rich text by a converter of our own (lines, lists, formatting, images, sounds, cloze deletions hidden on the front). One card per note, the files first, studysets as chosen (flatten, sub-studysets, or tags by subdeck), the notes' tags, and, if wanted, the review history, which FSRS replays: the first template's reviews to the card's schedule, a reversed note type's second to the reverse one. Imported cards say where they came from in their first revision (card schema 3); the dashboard doesn't count them as written. The test fixtures are exported by Anki itself (tool/anki/make_fixtures.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 04:52:02 +02:00
feat(app): the studyset screen laid out as before The Study pill reads "Learn 12 flashcards" in the app bar and "Learn 12" floating, "Study" with nothing due, and is off with nothing to study, saying so. With a path not finished, a chevron beside it offers to start or continue the path: a menu in the app bar, a Study mode sheet on phones. The floating bar rises above the licence line, which takes two lines at most. Add material offers a path, flashcards, a note and a PDF, in that order; imports moved to the options' Import content, which asks for a learning path or an Anki deck. The options read Edit, Move, Study options, Import content, Reset progress, Archive, Delete. The bibliography is an outlined round button above the content, and on phones at the foot of the topics. The tabs sit on a line of their own across the card, what the tab shown offers beside them: the flashcards' filter and options, and on the paths Reorder once there are two, which turns them into handles to drag. An empty tab says so in its middle; tapping the open tab again returns to its top, and pulling a tab down syncs. The breadcrumb's steps are plain links between "›", the studyset itself bold. A long description is cut at a fixed height, measured, with Read more opening it whole under the studyset's title. A studyset that no longer exists leads back to the library. The topics: All topics on the primary colour, in view also while they are rearranged; each topic with the study it invites in a round icon, what it holds ("Empty" with nothing, files counted too), and its options (now with Reset progress) behind "…" or a long press where the finger is; New sub-studyset at the foot below a line, the list with its scrollbar always shown. All topics opens Add material while nothing is due. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:42:50 +02:00
@override
String get studysetBackToLibrary => 'Back to library';
Import Anki decks with their files and history Files in content: the person's images and sounds as asset rows synced like all content, their files kept by hash on each device, uploaded after each sync, downloaded on demand and checked against their hash (core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in the editor's image block and an audio block of ours; any other URL is never fetched. Sounds play through audioplayers, one at a time. Content holding only an image or a sound isn't blank, and lists show it as such. The Anki import ("Import from Anki" under Add material): .apkg and .colpkg packages in all three formats Anki has written, read on an isolate of their own. Modern packages compress everything with zstd in frames that don't record their size, which pure_zstd v1.0.0 refuses; our fork decodes them. Fields become rich text by a converter of our own (lines, lists, formatting, images, sounds, cloze deletions hidden on the front). One card per note, the files first, studysets as chosen (flatten, sub-studysets, or tags by subdeck), the notes' tags, and, if wanted, the review history, which FSRS replays: the first template's reviews to the card's schedule, a reversed note type's second to the reverse one. Imported cards say where they came from in their first revision (card schema 3); the dashboard doesn't count them as written. The test fixtures are exported by Anki itself (tool/anki/make_fixtures.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 04:52:02 +02:00
@override
String get fileImage => 'Image';
@override
String get fileSound => 'Sound';
@override
String get fileImageUnavailable =>
'This image isn\'t on this device yet. It shows once it\'s uploaded and you\'re online.';
Fix what the first Android run found The flow ran on a Pixel 9a and its emulator (2026-10-02); what it found: - The composer stayed on the card just written after "New flashcard", and on the original after "Duplicate": leaving a card changed the state before the new one was in it, so the screen took the new card for gone. It now waits for the card it asked for. - Bottom sheets reached under the status bar with the keyboard open (useSafeArea). - The field being written stayed behind the keyboard: the editor scrolls only within itself, so the card face now keeps it in view when it opens and when the keyboard rises, as a text field does. - A tap on read-only rich text put a caret in it and opened the keyboard on phones, and in the composer never opened the field. RichContentView is display only now: no selection, taps go to what surrounds it, a sound's button still plays (decided 2026-10-02). - The card types broke their names on a phone; they are choice chips that wrap (decided 2026-10-02). - The week overview marked days before the person's first activity as missed; they show nothing now. An import starts the activity too, without counting as studying (decided 2026-10-02). - A composer preview cut short gave no sign of it; it fades out over its last 32 dp, only when cut short (decided 2026-10-02). - "Isn't on this device yet" showed for any image or sound that failed; a file that is here but can't be decoded now says so, and offers no retry. - The signed-in account card was narrower than the anonymous one. - The composer's title said "Create flashcards" also when opened on a stored card; it says "Edit flashcards in …" then (decided 2026-10-02). - The Anki fixtures carry a valid PNG and a silent MP3, so they also serve to try the import by hand (decided 2026-10-02); regenerated with Anki 26.9.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 19:52:39 +02:00
@override
String get fileImageUnreadable =>
'This image can\'t be shown. Its file may be damaged, or in a format this device can\'t read.';
Import Anki decks with their files and history Files in content: the person's images and sounds as asset rows synced like all content, their files kept by hash on each device, uploaded after each sync, downloaded on demand and checked against their hash (core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in the editor's image block and an audio block of ours; any other URL is never fetched. Sounds play through audioplayers, one at a time. Content holding only an image or a sound isn't blank, and lists show it as such. The Anki import ("Import from Anki" under Add material): .apkg and .colpkg packages in all three formats Anki has written, read on an isolate of their own. Modern packages compress everything with zstd in frames that don't record their size, which pure_zstd v1.0.0 refuses; our fork decodes them. Fields become rich text by a converter of our own (lines, lists, formatting, images, sounds, cloze deletions hidden on the front). One card per note, the files first, studysets as chosen (flatten, sub-studysets, or tags by subdeck), the notes' tags, and, if wanted, the review history, which FSRS replays: the first template's reviews to the card's schedule, a reversed note type's second to the reverse one. Imported cards say where they came from in their first revision (card schema 3); the dashboard doesn't count them as written. The test fixtures are exported by Anki itself (tool/anki/make_fixtures.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 04:52:02 +02:00
@override
String get fileFromElsewhere => 'Files from the web aren\'t shown.';
@override
String get filePlaySound => 'Play sound';
@override
String get fileStopSound => 'Stop sound';
@override
String get fileSoundUnavailable =>
'This sound isn\'t on this device yet. It plays once it\'s uploaded and you\'re online.';
Fix what the first Android run found The flow ran on a Pixel 9a and its emulator (2026-10-02); what it found: - The composer stayed on the card just written after "New flashcard", and on the original after "Duplicate": leaving a card changed the state before the new one was in it, so the screen took the new card for gone. It now waits for the card it asked for. - Bottom sheets reached under the status bar with the keyboard open (useSafeArea). - The field being written stayed behind the keyboard: the editor scrolls only within itself, so the card face now keeps it in view when it opens and when the keyboard rises, as a text field does. - A tap on read-only rich text put a caret in it and opened the keyboard on phones, and in the composer never opened the field. RichContentView is display only now: no selection, taps go to what surrounds it, a sound's button still plays (decided 2026-10-02). - The card types broke their names on a phone; they are choice chips that wrap (decided 2026-10-02). - The week overview marked days before the person's first activity as missed; they show nothing now. An import starts the activity too, without counting as studying (decided 2026-10-02). - A composer preview cut short gave no sign of it; it fades out over its last 32 dp, only when cut short (decided 2026-10-02). - "Isn't on this device yet" showed for any image or sound that failed; a file that is here but can't be decoded now says so, and offers no retry. - The signed-in account card was narrower than the anonymous one. - The composer's title said "Create flashcards" also when opened on a stored card; it says "Edit flashcards in …" then (decided 2026-10-02). - The Anki fixtures carry a valid PNG and a silent MP3, so they also serve to try the import by hand (decided 2026-10-02); regenerated with Anki 26.9.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 19:52:39 +02:00
@override
String get fileSoundUnplayable =>
'This sound can\'t be played. Its file may be damaged, or in a format this device can\'t play.';
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button Blocks beyond the editor's own, each built once for editor and view: - callout: eight kinds in theme colours, kind, title and "folded at first" from its header; written in place, left with Enter on its empty last line; never inside another - spoiler: written in plain sight, hidden behind Reveal read-only - formula on its own line, written on its own screen with a preview and snippet buttons, and formulas within a line (Σ in the format bar); set by flutter_math_fork behind MathView, LaTeX's numbered environments read as their aligned forms - image and sound picked from the device, kept as synced files - YouTube video from its link, with its start and a caption: a card until played, nothing fetched before; played in place where a web view exists (youtube_player_iframe, privacy-enhanced), else opened in the browser, behind YoutubeVideos in Dependencies - study button: a studyset, a tag or all cards, with or without its topics, back first; its StudyFocus rides on the study route and narrows that session only - tables, already in the editor, now insertable everywhere An Insert button opens on the format bar, so blocks can be inserted on phones too; the desktop's / menu lists the same blocks, translated. Card faces now dock the format bar while written, so cards get blocks and formatting on phones as well. The blocks are listed in EditorBlocks: the core's, plus the features' through EditorBlocksScope. Blocks of a type the app doesn't know say they need a newer version and stay as they are, so formatVersion stays 1. Their types and keys are in content_blocks.dart, which RichContent reads: a block's text counts as text, and a video or study button isn't blank. The path import now makes formulas real ones. integration_test/youtube_player_test.dart plays a real video through the app's player on a device, which no widget test can: YouTube refuses embeds that don't say where they are embedded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 12:20:41 +02:00
@override
String get fileImages => 'Images';
@override
String get fileSounds => 'Sounds';
@override
String get fileNotAnImage => 'This file isn\'t an image the app can show.';
@override
String get fileNotASound => 'This file isn\'t a sound the app can play.';
@override
String fileTooLarge(int megabytes) {
return 'This file is larger than $megabytes MB.';
}
@override
String get fileNotKept => 'The file couldn\'t be kept on this device.';
@override
String get fileRemoveImage => 'Remove image';
@override
String get fileRemoveSound => 'Remove sound';
@override
String get editorInsert => 'Insert';
@override
String get blockGroupText => 'Text';
@override
String get blockGroupBlocks => 'Blocks';
@override
String get blockGroupMedia => 'Media';
@override
String blockHeading(int level) {
return 'Heading $level';
}
@override
String get blockBulletedList => 'Bulleted list';
@override
String get blockNumberedList => 'Numbered list';
@override
String get blockChecklist => 'Checklist';
@override
String get blockQuote => 'Quote';
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
@override
String get blockListItem => 'List item';
@override
String get blockChecklistItem => 'To-do';
@override
String get editorSlashHint =>
'Type / to insert a block, or just start typing';
feat(app): editor blocks: callout, spoiler, formulas, files, video, study button Blocks beyond the editor's own, each built once for editor and view: - callout: eight kinds in theme colours, kind, title and "folded at first" from its header; written in place, left with Enter on its empty last line; never inside another - spoiler: written in plain sight, hidden behind Reveal read-only - formula on its own line, written on its own screen with a preview and snippet buttons, and formulas within a line (Σ in the format bar); set by flutter_math_fork behind MathView, LaTeX's numbered environments read as their aligned forms - image and sound picked from the device, kept as synced files - YouTube video from its link, with its start and a caption: a card until played, nothing fetched before; played in place where a web view exists (youtube_player_iframe, privacy-enhanced), else opened in the browser, behind YoutubeVideos in Dependencies - study button: a studyset, a tag or all cards, with or without its topics, back first; its StudyFocus rides on the study route and narrows that session only - tables, already in the editor, now insertable everywhere An Insert button opens on the format bar, so blocks can be inserted on phones too; the desktop's / menu lists the same blocks, translated. Card faces now dock the format bar while written, so cards get blocks and formatting on phones as well. The blocks are listed in EditorBlocks: the core's, plus the features' through EditorBlocksScope. Blocks of a type the app doesn't know say they need a newer version and stay as they are, so formatVersion stays 1. Their types and keys are in content_blocks.dart, which RichContent reads: a block's text counts as text, and a video or study button isn't blank. The path import now makes formulas real ones. integration_test/youtube_player_test.dart plays a real video through the app's player on a device, which no widget test can: YouTube refuses embeds that don't say where they are embedded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 12:20:41 +02:00
@override
String get blockDivider => 'Divider';
@override
String get blockTable => 'Table';
@override
String get blockRemove => 'Remove';
@override
String get blockUnknown => 'This part needs a newer version of the app.';
@override
String get blockCallout => 'Callout';
@override
String get blockSpoiler => 'Spoiler';
@override
String get calloutNote => 'Note';
@override
String get calloutInfo => 'Info';
@override
String get calloutTip => 'Tip';
@override
String get calloutSuccess => 'Success';
@override
String get calloutQuestion => 'Question';
@override
String get calloutWarning => 'Warning';
@override
String get calloutDanger => 'Danger';
@override
String get calloutQuote => 'Quote';
@override
String get calloutOptions => 'Callout options';
@override
String get calloutEditTitle => 'Edit title…';
@override
String get calloutFoldedAtFirst => 'Folded at first';
@override
String get calloutRemove => 'Remove callout';
@override
String get calloutShow => 'Show';
@override
String get calloutHide => 'Hide';
@override
String get calloutWriteInside => 'Write inside';
@override
String get calloutTitleDialog => 'Callout title';
@override
String get calloutTitleLabel => 'Title';
@override
String get calloutTitleHelper => 'Leave it empty to show the kind\'s name.';
@override
String get spoilerReveal => 'Reveal';
@override
String get spoilerPlaceholder => 'Hidden text, revealed on a tap';
@override
String get blockFormula => 'Formula';
@override
String get formatInlineMath => 'Formula in the line';
@override
String get formulaTitle => 'Formula';
@override
String get formulaInLineTitle => 'Formula in the line';
@override
String get formulaPreview => 'Preview';
@override
String get formulaPreviewEmpty => 'The formula shows here';
@override
String get formulaLatex => 'LaTeX';
@override
String get formulaLatexHelper =>
'\\\\ starts a new line in aligned, cases or a matrix.';
@override
String get formulaEmpty => 'An empty formula. Tap to write it.';
@override
String get formulaEdit => 'Edit formula';
@override
String get formulaRemove => 'Remove formula';
@override
String get blockYoutube => 'YouTube video';
@override
String get youtubeLink => 'YouTube link';
@override
String get youtubeLinkHelper => 'Paste a link from YouTube.';
@override
String get youtubeNotFound => 'No YouTube video in this link.';
@override
String get youtubeFound => 'Found the video.';
@override
String youtubeFoundFrom(String time) {
return 'Found the video, starting at $time.';
}
@override
String get youtubeCaption => 'Caption';
@override
String get youtubeCaptionHelper =>
'Shown in place of the video until it plays.';
@override
String get youtubeVideo => 'YouTube video';
@override
String get youtubeSource => 'YouTube';
@override
String youtubeFrom(String time) {
return 'YouTube · from $time';
}
@override
String get youtubeEdit => 'Edit video';
@override
String get youtubePlay => 'Play video';
@override
String get youtubeOpen => 'Open on YouTube';
@override
String get youtubeRemove => 'Remove video';
@override
String get youtubeBroken => 'This video\'s link can\'t be read.';
@override
String get youtubeNotOpened => 'Couldn\'t open YouTube.';
@override
String get blockStudyButton => 'Study button';
@override
String get studyButtonDefaultLabel => 'Study';
@override
String get studyButtonStudyset => 'Studyset to study';
@override
String get studyButtonTag => 'Cards';
@override
String get studyButtonAllCards => 'All cards';
@override
String get studyButtonLabel => 'Label';
@override
String get studyButtonTopics => 'With its topics';
@override
String get studyButtonBackFirst => 'Back first';
@override
String get studyButtonBackFirstHelper =>
'Front/back and vocabulary cards ask their back.';
@override
String studyButtonWithTag(String tag) {
return 'Tagged $tag';
}
@override
String get studyButtonAlone => 'Without its topics';
@override
String get studyButtonEdit => 'Edit study button';
@override
String get studyButtonRemove => 'Remove study button';
@override
String get studyButtonBroken =>
'This study button doesn\'t say what to study.';
@override
String get studyButtonGone => 'That studyset no longer exists.';
feat: PDF sources: read, take notes and cards from pages, deadlines PDFs live in a studyset's new Files tab: uploaded from Add material (a file the studyset holds already asks first), each row with its pages, cards, notes and size, how far it is read and its reading deadline; renamed, given a deadline (a page or a chapter by a day), marked as read, started afresh, deleted with a moment to undo. The reader (/pdfs/:id) opens where reading left off: on wide windows a rail of tools (search, fit, zoom, fullscreen), a page counter with the chapter, and a sidebar; on phones a top bar, a page scrubber and a sheet. The sidebar holds the PDF's flashcards, by page or chapter, made from selected text or written there, each tied to its page and tagged with its chapter; and the page's note, an ordinary note tied to the page. Screenshots of a region become images that know their page. Cards and screenshots lead back to their page; reading on past a chapter with cards suggests studying it. Home shows the deadlines running and to come, and a timeline of them. Server: pdf_source rows (content in the payload, last writer wins) and an append-only pdf_reading log, the furthest page derived from it so devices never conflict. Files up to 50 MB for PDFs (20 MB for others; a larger file that isn't a PDF is deleted once stored). A deleted PDF takes its file's asset row along unless another PDF has the same file. Card payload 4 and note payload 2 keep the page. The reader draws with pdfrx (PDFium) behind PdfEngine in Dependencies; widget tests use a fake, and integration_test/pdf_engine_test.dart checks the real engine on a device or the desktop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 14:40:08 +02:00
@override
String get pdfPageField => 'PDF page';
@override
String get studysetTabFiles => 'Files';
@override
String get pdfUploadMenu => 'Upload a PDF';
@override
String get pdfFileType => 'PDFs';
@override
String pdfAdded(String title) {
return 'Added \"$title\".';
}
@override
String get pdfNotPdf => 'This file isn\'t a PDF.';
@override
String pdfTooLarge(int megabytes) {
return 'This PDF is larger than $megabytes MB.';
}
@override
String get pdfDuplicateTitle => 'PDF already here';
@override
String pdfDuplicateMessage(String title) {
return 'This file is \"$title\", which this studyset holds already. Add it as a copy, or keep the one there?';
}
@override
String get pdfDuplicateKeep => 'Keep the one there';
@override
String get pdfDuplicateAdd => 'Add a copy';
@override
String get filesEmpty => 'No files yet';
@override
String get filesEmptyHint =>
'Use \"Add material → Upload a PDF\" to add one.';
@override
String get filesSort => 'Sort';
@override
String get filesSortCustom => 'Custom order';
feat: PDF sources: read, take notes and cards from pages, deadlines PDFs live in a studyset's new Files tab: uploaded from Add material (a file the studyset holds already asks first), each row with its pages, cards, notes and size, how far it is read and its reading deadline; renamed, given a deadline (a page or a chapter by a day), marked as read, started afresh, deleted with a moment to undo. The reader (/pdfs/:id) opens where reading left off: on wide windows a rail of tools (search, fit, zoom, fullscreen), a page counter with the chapter, and a sidebar; on phones a top bar, a page scrubber and a sheet. The sidebar holds the PDF's flashcards, by page or chapter, made from selected text or written there, each tied to its page and tagged with its chapter; and the page's note, an ordinary note tied to the page. Screenshots of a region become images that know their page. Cards and screenshots lead back to their page; reading on past a chapter with cards suggests studying it. Home shows the deadlines running and to come, and a timeline of them. Server: pdf_source rows (content in the payload, last writer wins) and an append-only pdf_reading log, the furthest page derived from it so devices never conflict. Files up to 50 MB for PDFs (20 MB for others; a larger file that isn't a PDF is deleted once stored). A deleted PDF takes its file's asset row along unless another PDF has the same file. Card payload 4 and note payload 2 keep the page. The reader draws with pdfrx (PDFium) behind PdfEngine in Dependencies; widget tests use a fake, and integration_test/pdf_engine_test.dart checks the real engine on a device or the desktop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 14:40:08 +02:00
@override
String get filesSortName => 'Name (A–Z)';
@override
String get filesSortNewest => 'Newest first';
@override
String get filesSortOldest => 'Oldest first';
@override
String get filesSortLargest => 'Largest first';
@override
String pdfPages(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count pages',
one: '1 page',
);
return '$_temp0';
}
@override
String pdfPercentRead(int percent) {
return '$percent% read';
}
@override
String get pdfDeadlineMet => 'Reading deadline met';
@override
String pdfDeadlineUpcoming(int days, int pages) {
String _temp0 = intl.Intl.pluralLogic(
days,
locale: localeName,
other: '$days days',
one: '1 day',
);
return 'Starts in $_temp0 — $pages/day planned';
}
@override
String pdfDeadlinePassed(int pages) {
String _temp0 = intl.Intl.pluralLogic(
pages,
locale: localeName,
other: '$pages pages',
one: '1 page',
);
return 'Deadline passed — $_temp0 left';
}
@override
String pdfDeadlineToday(int pages) {
String _temp0 = intl.Intl.pluralLogic(
pages,
locale: localeName,
other: '$pages pages',
one: '1 page',
);
return 'Due today — $_temp0 left';
}
@override
String pdfDeadlinePace(int pages, int target, int days) {
String _temp0 = intl.Intl.pluralLogic(
days,
locale: localeName,
other: '$days days',
one: '1 day',
);
return '$pages/day to reach page $target in $_temp0';
}
@override
String get pdfOptions => 'Actions';
@override
String get pdfRename => 'Rename';
@override
String get pdfRenameTitle => 'Rename PDF';
@override
String get pdfRenameLabel => 'Title';
@override
String get pdfSetDeadline => 'Set reading deadline';
@override
String get pdfEditDeadline => 'Edit reading deadline';
@override
String get pdfMarkFinished => 'Mark as finished';
@override
String get pdfResetReading => 'Reset reading progress';
@override
String get pdfResetTitle => 'Reset reading progress?';
@override
String pdfResetMessage(String title) {
return 'How far you\'ve read \"$title\" is forgotten. The PDF, its flashcards and notes stay.';
}
@override
String get pdfResetConfirm => 'Reset';
@override
String get pdfMoreInfo => 'More info';
@override
String get pdfInfoFileName => 'File name';
@override
String get pdfInfoSize => 'Size';
@override
String get pdfInfoPages => 'Pages';
@override
String get pdfInfoUnknown => 'Known once it is opened';
@override
String get pdfDelete => 'Delete';
@override
String get pdfDeleteTitle => 'Delete PDF?';
@override
String pdfDeleteMessage(String title) {
return '\"$title\" is removed from this studyset, with how far it was read. Flashcards and notes made from it stay.';
}
@override
String get pdfDeleteConfirm => 'Delete';
@override
String get pdfDeleted => 'PDF deleted';
@override
String get deadlineSheetSet => 'Set a reading deadline';
@override
String get deadlineSheetEdit => 'Edit reading deadline';
@override
String get deadlineSheetHelp =>
'Reach a page by a day. Progress follows the furthest page you reach as you read.';
@override
String deadlineReachPageOf(int count) {
return 'Reach page (of $count)';
}
@override
String get deadlineReachPage => 'Reach page';
@override
String get deadlineDecrease => 'Decrease';
@override
String get deadlineIncrease => 'Increase';
@override
String get deadlineChapter => 'Or finish a chapter';
@override
String deadlineChapterEntry(String title, int page) {
return '$title · to p.$page';
}
@override
String get deadlineBy => 'By';
@override
String get deadlineStart => 'Start on';
@override
String get deadlineStartToday => 'Today';
@override
String get deadlineStartReset => 'Start today';
@override
String get deadlineStartHelp =>
'The daily pace counts from this day on. Until then the PDF waits under \"Upcoming\".';
@override
String get deadlineSave => 'Save deadline';
@override
String get deadlineRemove => 'Remove deadline';
@override
String get readerMissing =>
'This PDF isn\'t on this device yet. It opens once the device that added it has uploaded it and you\'re online.';
@override
String get readerUnreadable => 'This file can\'t be read as a PDF.';
@override
String get readerBack => 'Back to studyset';
@override
String get readerSearch => 'Search PDF';
@override
String get readerFitWidth => 'Fit width';
@override
String get readerFitHeight => 'Fit height';
@override
String get readerZoomIn => 'Zoom in';
@override
String get readerZoomOut => 'Zoom out';
@override
String get readerSetZoom => 'Set zoom';
@override
String get readerZoomTitle => 'Zoom';
@override
String get readerZoomApply => 'Apply';
@override
String get readerFullscreen => 'Fullscreen';
@override
String get readerExitFullscreen => 'Exit fullscreen';
@override
String get readerPreviousPage => 'Previous page';
@override
String get readerNextPage => 'Next page';
@override
String readerPageOf(int count) {
return '/ $count';
}
@override
String get readerFindHint => 'Find in PDF';
@override
String readerMatchOf(int current, int total) {
return '$current of $total';
}
@override
String get readerPreviousMatch => 'Previous match';
@override
String get readerNextMatch => 'Next match';
@override
String get readerCloseSearch => 'Close search';
@override
String get readerExpand => 'Expand';
@override
String get readerCollapse => 'Collapse';
@override
String get readerTabFlashcards => 'Flashcards';
@override
String get readerTabNotes => 'Notes';
@override
String get readerCardCreated => 'Flashcard created';
@override
String get readerCardDeleteTitle => 'Delete flashcard?';
@override
String get readerCardDeleteMessage =>
'The flashcard is deleted, with how it was studied.';
@override
String get readerCardDeleteConfirm => 'Delete';
@override
String get readerFrontMatter => 'Front matter';
@override
String readerPage(int page) {
return 'Page $page';
}
@override
String get readerByPage => 'Page';
@override
String get readerByChapter => 'Chapter';
@override
String get readerCardsEmpty => 'Select text to make flashcards in no time';
@override
String get readerCardsEmptyHint =>
'Select the lines that matter and send them to a question or an answer, or press \"Create flashcard\".';
@override
String get readerCreateCard => 'Create flashcard';
@override
String get readerEditCard => 'Edit flashcard';
@override
String get readerBackToCards => 'Back to flashcards';
@override
String readerReferencedPage(int page) {
return 'Referenced page: $page';
}
@override
String readerUseCurrentPage(int page) {
return 'Use current ($page)';
}
@override
String get readerQuestion => 'Question';
@override
String get readerAnswer => 'Answer';
@override
String get readerKeepOpen => 'Keep creation mode open';
@override
String get readerClear => 'Clear';
@override
String get readerCreate => 'Create';
@override
String get readerUntitledCard => 'Untitled flashcard';
@override
String get readerCardOptions => 'Card options';
@override
String get readerNoteSaved => 'Saved';
@override
String get readerNoteEditing => 'Editing…';
@override
String get readerNoteSaving => 'Saving…';
@override
String get readerNoteFailed => 'Save failed';
@override
String get readerToQuestion => 'Question';
@override
String get readerToAnswer => 'Answer';
@override
String get readerToNote => 'Notes';
@override
String get readerLineBreaksJoined => 'Line breaks joined (tap to keep)';
@override
String get readerLineBreaksKept => 'Line breaks kept (tap to join)';
@override
String get blockScreenshot => 'Screenshot of the page';
@override
String get readerCaptureCancel => 'Cancel screenshot';
@override
String get readerCaptureHint => 'Drag over the part of the page to keep';
@override
String get readerCaptureFailed =>
'The page couldn\'t be drawn. Try again once it has loaded.';
@override
String get readerScreenshotSaved => 'Screenshot saved';
@override
String get imageOpenPdfPage => 'Open the PDF at this page';
@override
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
String get homeWidgetDeadlines => 'PDF reading deadlines';
feat: PDF sources: read, take notes and cards from pages, deadlines PDFs live in a studyset's new Files tab: uploaded from Add material (a file the studyset holds already asks first), each row with its pages, cards, notes and size, how far it is read and its reading deadline; renamed, given a deadline (a page or a chapter by a day), marked as read, started afresh, deleted with a moment to undo. The reader (/pdfs/:id) opens where reading left off: on wide windows a rail of tools (search, fit, zoom, fullscreen), a page counter with the chapter, and a sidebar; on phones a top bar, a page scrubber and a sheet. The sidebar holds the PDF's flashcards, by page or chapter, made from selected text or written there, each tied to its page and tagged with its chapter; and the page's note, an ordinary note tied to the page. Screenshots of a region become images that know their page. Cards and screenshots lead back to their page; reading on past a chapter with cards suggests studying it. Home shows the deadlines running and to come, and a timeline of them. Server: pdf_source rows (content in the payload, last writer wins) and an append-only pdf_reading log, the furthest page derived from it so devices never conflict. Files up to 50 MB for PDFs (20 MB for others; a larger file that isn't a PDF is deleted once stored). A deleted PDF takes its file's asset row along unless another PDF has the same file. Card payload 4 and note payload 2 keep the page. The reader draws with pdfrx (PDFium) behind PdfEngine in Dependencies; widget tests use a fake, and integration_test/pdf_engine_test.dart checks the real engine on a device or the desktop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-03 14:40:08 +02:00
@override
String get deadlinesTitle => 'Reading deadlines';
@override
String get deadlinesCurrent => 'Current';
@override
String get deadlinesUpcoming => 'Upcoming';
@override
String deadlinesProgress(int furthest, int target, String when) {
return 'p.$furthest/$target · $when';
}
@override
String get deadlinePassedShort => 'deadline passed';
@override
String get deadlineDueTodayShort => 'due today';
@override
String deadlineDaysLeft(int days) {
String _temp0 = intl.Intl.pluralLogic(
days,
locale: localeName,
other: '$days days left',
one: '1 day left',
);
return '$_temp0';
}
@override
String deadlineToPageToday(int page) {
return 'to p.$page today';
}
@override
String deadlineStartsIn(int days) {
String _temp0 = intl.Intl.pluralLogic(
days,
locale: localeName,
other: '$days days',
one: '1 day',
);
return 'Starts in $_temp0';
}
@override
String get deadlinesPlaceholder => 'Appears when you have reading deadlines';
@override
String get deadlinesOpenTimeline => 'Open the timeline';
@override
String get timelineTitle => 'Reading timeline';
@override
String get timelineEmpty => 'No reading deadlines yet';
@override
String get timelineEmptyHint =>
'Set a reading deadline on a PDF to see it here.';
@override
String get timelineToday => 'Today';
@override
String timelinePages(int furthest, int target) {
return 'p.$furthest/$target';
}
@override
String chapterFinished(String chapter, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count cards',
one: 'card',
);
return 'Finished “$chapter” — study the $_temp0 you made?';
}
@override
String get chapterStudyNow => 'Study now';
@override
String get chapterDismiss => 'Dismiss';
@override
String get deadlineChapterLoading => 'Loading chapters…';
@override
String fileSizeKb(int size) {
return '$size KB';
}
@override
String fileSizeMb(String size) {
return '$size MB';
}
@override
String pdfPageShort(int page) {
return 'p. $page';
}
Import Anki decks with their files and history Files in content: the person's images and sounds as asset rows synced like all content, their files kept by hash on each device, uploaded after each sync, downloaded on demand and checked against their hash (core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in the editor's image block and an audio block of ours; any other URL is never fetched. Sounds play through audioplayers, one at a time. Content holding only an image or a sound isn't blank, and lists show it as such. The Anki import ("Import from Anki" under Add material): .apkg and .colpkg packages in all three formats Anki has written, read on an isolate of their own. Modern packages compress everything with zstd in frames that don't record their size, which pure_zstd v1.0.0 refuses; our fork decodes them. Fields become rich text by a converter of our own (lines, lists, formatting, images, sounds, cloze deletions hidden on the front). One card per note, the files first, studysets as chosen (flatten, sub-studysets, or tags by subdeck), the notes' tags, and, if wanted, the review history, which FSRS replays: the first template's reviews to the card's schedule, a reversed note type's second to the reverse one. Imported cards say where they came from in their first revision (card schema 3); the dashboard doesn't count them as written. The test fixtures are exported by Anki itself (tool/anki/make_fixtures.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 04:52:02 +02:00
@override
String get ankiImportMenu => 'Import from Anki';
@override
String get ankiImportFileType => 'Anki decks';
@override
String get ankiImportTitle => 'Import Anki deck';
@override
String get ankiImportOptionsTitle => 'How should subdecks be imported?';
@override
String get ankiImportReading => 'Reading the deck…';
@override
String ankiImportSummary(int cards) {
String _temp0 = intl.Intl.pluralLogic(
cards,
locale: localeName,
other: '$cards cards.',
one: '1 card.',
);
return '$_temp0';
}
@override
String ankiImportSummaryDecks(int cards, int decks) {
String _temp0 = intl.Intl.pluralLogic(
cards,
locale: localeName,
other: '$cards cards',
one: '1 card',
);
return '$_temp0 across $decks subdecks.';
}
@override
String get ankiImportModeFlatten => 'Flatten into this studyset';
@override
String get ankiImportModeFlattenSubtitle =>
'Import every card here and ignore the subdecks.';
@override
String get ankiImportModeSubStudysets => 'Recreate as sub-studysets';
@override
String get ankiImportModeSubStudysetsSubtitle =>
'Mirror the subdeck tree: each subdeck becomes a nested studyset.';
@override
String get ankiImportModeTag => 'Tag cards by subdeck';
@override
String get ankiImportModeTagSubtitle =>
'Keep every card here, tagged with each subdeck it came from.';
@override
String get ankiImportProgress => 'Import study progress';
@override
String get ankiImportProgressSubtitle =>
'Bring Anki\'s review history over: cards continue where they left off. New cards stay new.';
@override
String ankiImportMedia(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Include media ($count files)',
one: 'Include media (1 file)',
);
return '$_temp0';
}
@override
String get ankiImportMediaSubtitle =>
'Keep the deck\'s images and sounds on the cards.';
@override
String ankiImportMediaMayNotFit(String size, String left) {
return 'Its files take $size MB, more than the $left MB you have left: those that don\'t fit stay on this device, and your other devices won\'t show them.';
}
Import Anki decks with their files and history Files in content: the person's images and sounds as asset rows synced like all content, their files kept by hash on each device, uploaded after each sync, downloaded on demand and checked against their hash (core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in the editor's image block and an audio block of ours; any other URL is never fetched. Sounds play through audioplayers, one at a time. Content holding only an image or a sound isn't blank, and lists show it as such. The Anki import ("Import from Anki" under Add material): .apkg and .colpkg packages in all three formats Anki has written, read on an isolate of their own. Modern packages compress everything with zstd in frames that don't record their size, which pure_zstd v1.0.0 refuses; our fork decodes them. Fields become rich text by a converter of our own (lines, lists, formatting, images, sounds, cloze deletions hidden on the front). One card per note, the files first, studysets as chosen (flatten, sub-studysets, or tags by subdeck), the notes' tags, and, if wanted, the review history, which FSRS replays: the first template's reviews to the card's schedule, a reversed note type's second to the reverse one. Imported cards say where they came from in their first revision (card schema 3); the dashboard doesn't count them as written. The test fixtures are exported by Anki itself (tool/anki/make_fixtures.py). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-02 04:52:02 +02:00
@override
String get ankiImportRunning => 'Importing…';
@override
String get ankiImportConfirm => 'Import';
@override
String get ankiImportNotAPackage =>
'This isn\'t an Anki deck. In Anki, export a deck as an .apkg file and import that.';
@override
String get ankiImportNewerFormat =>
'This deck comes from a newer Anki than this app knows. Export it again with “Support older Anki versions”.';
@override
String get ankiImportUnreadable => 'This deck is damaged and can\'t be read.';
@override
String get ankiImportBrokeOff =>
'The import broke off. The cards it brought in so far stay.';
@override
String ankiImported(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Imported $count flashcards from Anki.',
one: 'Imported 1 flashcard from Anki.',
);
return '$_temp0';
}
@override
String ankiImportedSubStudysets(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Imported $count cards into sub-studysets.',
one: 'Imported 1 card into sub-studysets.',
);
return '$_temp0';
}
@override
String ankiImportedTagged(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Imported $count cards, tagged by subdeck.',
one: 'Imported 1 card, tagged by subdeck.',
);
return '$_temp0';
}
@override
String ankiImportFilesSkipped(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count media files couldn\'t be imported.',
one: '1 media file couldn\'t be imported.',
);
return '$_temp0';
}
feat(app): a Pomodoro timer on Home, in the notification and in the top bar Home has a Pomodoro timer (decided 2026-10-07, after Goodtime): the profile it runs, chosen there while no timer is set, the time left of the phase, and Start, Pause or Resume, Skip and Stop. While studying, a small countdown sits in the study screen's header, and a tap pauses it or goes on. A phase that ends leads to the next, which starts by itself where the profile says so and otherwise waits, paused at its full length. Profiles follow the person to every device, in their settings (schema 4, still written as schema 3 while the profiles are as they come, so older apps go on reading the settings): Classic, 25 minutes of focus, 5 of break and 15 after four, and 52/17 without long breaks, built in; both can be changed, more added and deleted, from Home's card or from Settings. A profile can start breaks and focus by themselves, and keep the device quiet while focusing. The running timer is the device's own, kept in the person's folder: closed and opened again, the app catches up with the phases that ran out meanwhile. Android: the app's own foreground service (Kotlin, specialUse, as Goodtime does) shows an ongoing notification counting the phase down, with Pause or Resume, Skip and Stop; on Android 16 it is a Live Update, its progress bar the profile's cycle, each phase a segment, focus in cobalt and breaks in copper. An exact alarm (USE_EXACT_ALARM) alerts with sound and vibration when the phase ends, also with the app closed, and Do Not Disturb goes on while focusing once the person allowed it, the filter before given back afterwards. No notification plugin: flutter_local_notifications has Live Updates only in a prerelease that needs compileSdk 37; androidx.core 1.18, which the build resolved already, has every API needed. Linux, over the session bus (package:dbus, already in the build): where the desktop shows icons in its top bar (GNOME with the AppIndicator extension, which Ubuntu has built in; KDE) the timer counts down there, the buttons in the icon's menu, as a StatusNotifierItem written in Dart on the names and paths the snap's desktop plug allows; elsewhere a notification with the buttons says until when the phase runs. An alert with sound ends each phase while the app runs, and GNOME's banners stay off while focusing, through gsettings. xdg-desktop-portal's Background status, the planned fallback, serves Flatpaks only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:54:52 +02:00
@override
String get pomodoroTitle => 'Pomodoro';
@override
String get pomodoroFocus => 'Focus';
@override
String get pomodoroShortBreak => 'Short break';
@override
String get pomodoroLongBreak => 'Long break';
@override
String pomodoroFocusOf(int done, int total) {
return 'Focus $done of $total';
}
@override
String get pomodoroStart => 'Start';
@override
String get pomodoroPause => 'Pause';
@override
String get pomodoroResume => 'Resume';
@override
String get pomodoroSkip => 'Skip';
@override
String get pomodoroStop => 'Stop';
@override
String get pomodoroPaused => 'Paused';
@override
String pomodoroPausedProfile(String profile) {
return 'Paused · $profile';
}
@override
String get pomodoroClassic => 'Classic';
@override
String get pomodoroUnnamed => 'Unnamed profile';
@override
String get pomodoroProfiles => 'Pomodoro profiles';
@override
String get pomodoroChooseProfile => 'Choose a profile';
@override
String get pomodoroAddProfile => 'Add a profile';
@override
String get pomodoroEditProfile => 'Edit profile';
@override
String get pomodoroName => 'Name';
@override
String get pomodoroFocusLength => 'Focus';
@override
String get pomodoroShortBreakLength => 'Short break';
@override
String get pomodoroLongBreakLength => 'Long break';
@override
String pomodoroMinutes(int count) {
return '$count min';
}
@override
String get pomodoroNoLongBreak => 'None';
@override
String get pomodoroLongBreakAfter => 'Focus phases before a long break';
@override
String get pomodoroAutoStartBreaks => 'Start breaks by themselves';
@override
String get pomodoroAutoStartFocus => 'Start focus by itself after a break';
@override
String get pomodoroDoNotDisturb => 'Do Not Disturb while focusing';
@override
String get pomodoroDoNotDisturbHint =>
'The device keeps quiet while you focus; the alert at its end still sounds.';
@override
String get pomodoroDoNotDisturbDenied =>
'The app may not keep this device quiet. Allow it in the system\'s settings.';
@override
String get pomodoroDeleteProfile => 'Delete profile';
@override
String pomodoroSummary(int focus, int shortBreak) {
return '$focus min focus, $shortBreak min break';
}
@override
String pomodoroSummaryLong(
int focus,
int shortBreak,
int longBreak,
int after,
) {
return '$focus min focus, $shortBreak min break, $longBreak min after $after';
}
@override
String get pomodoroAlertFocusDone => 'Time for a break';
@override
String get pomodoroAlertBreakDone => 'Back to focus';
@override
String pomodoroAlertStarts(String phase) {
return '$phase has started.';
}
@override
String pomodoroAlertWaits(String phase) {
return '$phase is ready when you are.';
}
@override
String get homeWidgetPomodoro => 'Pomodoro timer';
@override
String get pomodoroAlerts => 'Pomodoro alerts';
@override
String pomodoroUntil(String time) {
return 'Until $time';
}
@override
String pomodoroLeft(String left) {
return '$left left';
}
@override
String pathImportAtStep(int step, String problem) {
return 'Step $step: $problem';
}
@override
String pathImportNotJson(String detail) {
return 'This is no JSON: $detail';
}
@override
String get pathImportNoPath => 'a \"path\" object is missing.';
@override
String get pathImportNoTitle => 'the path has no \"title\".';
@override
String get pathImportDescriptionNotText =>
'the path\'s \"description\" is no text.';
@override
String get pathImportNotAnObject => 'no object.';
@override
String pathImportUnknownStepType(String detail) {
return 'unknown \"type\": $detail.';
}
@override
String pathImportRightOptionMissing(String detail) {
return 'its right option $detail is not among its options.';
}
@override
String get pathImportRightOptionsMissing =>
'it names a right option that is not among its options.';
@override
String get pathImportNoPairing => 'no \"correct_pairing\".';
@override
String pathImportPairingTargetMissing(String detail) {
return 'it pairs items with a target that isn\'t there: $detail.';
}
@override
String get pathImportPairingIncomplete =>
'every item must be paired with one target.';
@override
String get pathImportNoPassage => 'no \"passage\".';
@override
String get pathImportBlanksMismatch =>
'it needs one word in \"blanks\" for each ___ in its passage.';
@override
String get pathImportBlockWithoutType => 'a block without \"type\".';
@override
String get pathImportMalformedLevel => 'a heading with a malformed level.';
@override
String get pathImportMalformedListItem => 'a malformed list item.';
@override
String get pathImportMalformedMath => 'a malformed math block.';
@override
String get pathImportMalformedCode => 'a malformed code block.';
@override
String pathImportUnknownBlock(String detail) {
return 'an unknown block: $detail.';
}
@override
String pathImportTextlessBlock(String detail) {
return 'a $detail without \"text\" or \"runs\".';
}
@override
String get pathImportMalformedRun => 'a malformed run of text.';
@override
String get pathImportListExpected => 'a list was expected.';
@override
String pathImportNotText(String detail) {
return '\"$detail\" must be text.';
}
@override
String pathImportTooFew(String detail) {
return 'it needs at least two \"$detail\".';
}
@override
String get pathImportMalformedNumber => 'a malformed number.';
@override
String get pathImportTitleNotText => 'a title must be text.';
@override
String get pathImportMalformedSource =>
'a source must be an object or text, and its parts text.';
@override
String get pathImportMalformedTable =>
'a table needs \"rows\" of cells, each as long as the first.';
@override
String pathImportMalformedCallout(String detail) {
return 'a callout with a malformed $detail.';
}
@override
String pathImportNotInCallout(String detail) {
return 'a $detail can\'t be inside a callout.';
}
@override
String get pomodoroTopBarHint =>
'The timer can count down in GNOME\'s top bar, with its buttons, once the AppIndicator extension is on.';
@override
String get pomodoroTopBarSetUp => 'Set up';
@override
String get pomodoroTopBarDismiss => 'Don\'t suggest again';
@override
String get cardBrowserTitle => 'Flashcards';
@override
String cardBrowserTitleWithName(String title) {
return '$title — flashcards';
}
@override
String get cardBrowserWithSubstudysets => 'With its sub-studysets';
@override
String get cardBrowserBrowse => 'Browse';
@override
String get cardBrowserBrowseTooltip => 'Browse flashcards';
@override
String get cardBrowserStudy => 'Study';
@override
String get cardBrowserEditor => 'Editor';
@override
String get cardBrowserMore => 'More';
@override
String get cardBrowserImport => 'Import flashcards';
@override
String get cardBrowserGrid => 'Grid';
@override
String get cardBrowserDetail => 'Detail';
@override
String get cardBrowserSearch => 'Search';
@override
String get cardBrowserSort => 'Sort';
@override
String get cardBrowserFilter => 'Filter';
@override
String get cardBrowserSelect => 'Select';
@override
String get cardBrowserSortManual => 'Manual order';
@override
String get cardBrowserSortNewest => 'Newest first';
@override
String get cardBrowserSortOldest => 'Oldest first';
@override
String get cardBrowserSortAlphabetical => 'A–Z (front)';
@override
String get cardBrowserSortDue => 'Due first';
@override
String get cardBrowserSearchHint => 'Search flashcards';
@override
String get cardBrowserCloseSearch => 'Close search';
@override
String get cardBrowserClearQuery => 'Clear query';
@override
String get cardBrowserFilterTitle => 'Filter flashcards';
@override
String get cardBrowserFilterType => 'Type';
@override
String get cardBrowserTypeAll => 'All cards';
@override
String get cardBrowserTypeFrontBack => 'Q&A only';
@override
String get cardBrowserTypeMultipleChoice => 'Multiple-choice only';
@override
String get cardBrowserTypeVocabulary => 'Words only';
@override
String get cardBrowserTypeMatching => 'Matching only';
@override
String get cardBrowserTypeMap => 'Maps only';
@override
String get cardBrowserTypeCharacter => 'Characters only';
@override
String get cardBrowserFilterTag => 'Tag';
@override
String get cardBrowserAllTags => 'All tags';
@override
String get cardBrowserFilterStatus => 'Status';
@override
String get cardBrowserStatusNew => 'New';
@override
String get cardBrowserStatusLearning => 'Learning';
@override
String get cardBrowserStatusDue => 'Due';
@override
String get cardBrowserStatusBuried => 'Buried';
@override
String get cardBrowserStatusReported => 'Reported';
@override
String get cardBrowserClearAll => 'Clear all';
@override
String get cardBrowserApply => 'Apply';
@override
String get cardBrowserEmptyTitle => 'No flashcards yet';
@override
String get cardBrowserEmptyHint => 'Create some to browse them here.';
@override
String get cardBrowserCreate => 'Create flashcards';
@override
String get cardBrowserNoMatches => 'No flashcards match';
@override
String get cardBrowserNoMatchesHint => 'Try a different search or filter.';
@override
String get cardBrowserClearFilters => 'Clear search & filters';
@override
String get cardBrowserRetry => 'Retry';
@override
String get cardBrowserPrevious => 'Previous';
@override
String get cardBrowserNext => 'Next';
@override
String get cardBrowserBuried => 'Buried';
@override
String get cardBrowserSelectAll => 'Select all';
@override
String get cardBrowserDeselectAll => 'Deselect all';
@override
String cardBrowserSelected(int count) {
return '$count selected';
}
@override
String get cardBrowserRangeHintTouch =>
'Long-press another card to select everything in between.';
@override
String get cardBrowserRangeHintPointer =>
'Shift-click another card to select everything in between.';
@override
String get cardBrowserMove => 'Move';
@override
String get cardBrowserAddTags => 'Add tags';
@override
String get cardBrowserRemoveTags => 'Remove tags';
@override
String get cardBrowserResetProgress => 'Reset progress';
@override
String get cardBrowserDelete => 'Delete';
@override
String get cardBrowserMoreActions => 'More actions';
@override
String get cardBrowserDone => 'Done';
@override
String cardBrowserDeleted(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards deleted',
one: '1 flashcard deleted',
);
return '$_temp0';
}
@override
String cardBrowserMoveTitle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Move $count flashcards to',
one: 'Move 1 flashcard to',
);
return '$_temp0';
}
@override
String cardBrowserMoved(int count, String title) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Moved $count flashcards to “$title”',
one: 'Moved 1 flashcard to “$title”',
);
return '$_temp0';
}
@override
String cardBrowserAddTagsTitle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Add tags to $count flashcards',
one: 'Add tags to 1 flashcard',
);
return '$_temp0';
}
@override
String cardBrowserAddTo(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Add to $count flashcards',
one: 'Add to 1 flashcard',
);
return '$_temp0';
}
@override
String cardBrowserRemoveTagsTitle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Remove tags from $count flashcards',
one: 'Remove tags from 1 flashcard',
);
return '$_temp0';
}
@override
String cardBrowserRemoveFrom(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Remove from $count flashcards',
one: 'Remove from 1 flashcard',
);
return '$_temp0';
}
@override
String get cardBrowserNoTags =>
'No tags yet. Create tags from a flashcard\'s editor, then apply them here.';
@override
String get cardBrowserNoTagsOnCards => 'The flashcards chosen have no tags.';
@override
String cardBrowserTagsAdded(int tags, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards',
one: '1 flashcard',
);
String _temp1 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards',
one: '1 flashcard',
);
String _temp2 = intl.Intl.pluralLogic(
tags,
locale: localeName,
other: 'Added $tags tags to $_temp0',
one: 'Added 1 tag to $_temp1',
);
return '$_temp2';
}
@override
String cardBrowserTagsRemoved(int tags, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards',
one: '1 flashcard',
);
String _temp1 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards',
one: '1 flashcard',
);
String _temp2 = intl.Intl.pluralLogic(
tags,
locale: localeName,
other: 'Removed $tags tags from $_temp0',
one: 'Removed 1 tag from $_temp1',
);
return '$_temp2';
}
@override
String cardBrowserResetTitle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Start $count flashcards afresh?',
one: 'Start 1 flashcard afresh?',
);
return '$_temp0';
}
@override
String get cardBrowserResetMessage =>
'They count as new again. Their reviews stay in your history and streaks.';
@override
String get cardBrowserResetConfirm => 'Reset';
@override
String cardBrowserReset(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count flashcards start afresh',
one: '1 flashcard starts afresh',
);
return '$_temp0';
}
@override
String get studysetShare => 'Share';
@override
String get studysetSharePrivate => 'Share: private';
@override
String get studysetSharePublished => 'Share: published';
@override
String get publishTitle => 'Publish';
@override
String get publishPrivate => 'Private · only you can see it';
@override
String publishPublished(int version, String date) {
return 'Published · v$version · $date';
}
@override
String get publishIntro =>
'A release is this studyset with everything inside it, as it is now. Others can find it, learn from it and copy it. Each release stays as it was; later changes go into the next one.';
@override
String get publishAnonymous =>
'Sign in with GitHub to have a public profile and publish.';
@override
String get publishName => 'Name';
@override
String get publishNameHint => 'japanese';
@override
String publishLocation(String location) {
return 'Found at $location';
}
@override
String get publishNameFixed =>
'Fixed since you first published: others refer to it.';
@override
String get publishLicence => 'Licence';
@override
String get publishLicenceNone => 'None';
@override
String get publishLicenceOpen => 'Allows publishing.';
@override
String get publishLicenceNoDerivatives =>
'Allows publishing as it is: learners keep their changes to themselves.';
@override
String get publishLicenceClosed =>
'Doesn\'t allow passing it on. Choose another under Edit.';
@override
String publishLicenceNotOpen(String titles) {
return 'These studysets\' licences don\'t allow passing them on: $titles';
}
@override
String get publishNotes => 'What changed';
@override
String get publishNotesHint => 'New in this release…';
@override
String get publishDescription => 'Description on Explore';
@override
String get publishDescriptionAdd => 'Add a description';
@override
String get publishDescriptionHelp =>
'What people see on Explore before adding it. It starts as the studyset’s own; the studyset keeps its own as it is.';
@override
String get publishReleases => 'Releases';
@override
String publishRelease(int version, String date) {
return 'v$version · $date';
}
@override
String publishButton(int version) {
return 'Publish v$version';
}
@override
String get publishNoProfile =>
'A release names you by your public profile. Make one first.';
@override
String get publishMakeProfile => 'Make a public profile';
@override
String get publishConflicts =>
'These were changed differently on two devices. Settle them first:';
@override
String get publishConflictsElsewhere =>
'Some cards, notes or steps were changed differently on two devices. Sync, then try again.';
@override
String get publishUntitled => 'Untitled';
@override
String get publishFilesMissing =>
'Some files haven\'t reached your instance yet. Try again in a moment.';
@override
String get publishNothingChanged =>
'Nothing has changed since the last release.';
@override
String get publishBusy =>
'This studyset is being published already. Try again in a moment.';
@override
String get publishSlugInvalid =>
'Lower-case letters, digits and hyphens, starting with a letter or digit.';
@override
String get publishSlugTaken => 'Another of your studysets has this name.';
@override
String get publishNotesTooLong => 'At most 5,000 characters.';
@override
String publishDone(int version) {
return 'Published v$version.';
}
@override
String get publishFailed =>
'Couldn\'t reach your instance. Nothing was published.';
@override
String get publishUnreachable => 'Publishing can\'t be reached right now.';
@override
String get publishRetry => 'Retry';
@override
String get publishCopyLocation => 'Copy address';
@override
String get publishLocationCopied => 'Address copied.';
@override
String get publishedField => 'Release';
@override
String publishedVersion(int version) {
return 'v$version';
}
@override
String get exploreSearch => 'Search titles and authors';
@override
String get exploreLanguages => 'Languages';
@override
String get exploreAnyLanguage => 'Any';
@override
String exploreLanguagePair(String front, String back) {
return '$front → $back';
}
@override
String get exploreKinds => 'Content';
@override
String exploreKindsChosen(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count kinds of content',
one: '1 kind of content',
);
return '$_temp0';
}
@override
String get exploreKindCards => 'Flashcards';
@override
String get exploreKindNotes => 'Notes';
@override
String get exploreKindPaths => 'Learning paths';
@override
String get exploreKindPdfs => 'PDFs';
@override
String get exploreKindsIntro => 'Only studysets holding each of these.';
@override
String get exploreClearFilters => 'Clear filters';
@override
String get exploreNothingMatches => 'Nothing published here matches.';
@override
String exploreBy(String author) {
return 'by $author';
}
@override
String exploreRelease(int version, String date) {
return 'v$version · $date';
}
@override
String exploreLatestRelease(int version, String date) {
return 'Latest release: v$version · $date';
}
@override
String get exploreAddToLibrary => 'Add to library';
@override
String get exploreAdd => 'Add';
@override
String get exploreInLibraryShort => 'In library';
@override
String get exploreInLibrary => 'In your library';
@override
String explorePercent(int percent) {
return '$percent %';
}
@override
String get exploreAdded => 'Added to your library.';
@override
String get exploreOpen => 'Open';
@override
String get exploreNeedsNewerApp =>
'This studyset needs a newer version of the app.';
@override
String get exploreFailed => 'Couldn\'t reach your instance. Try again.';
@override
String get exploreUnreachable => 'Explore can\'t be reached right now.';
@override
String get exploreRetry => 'Retry';
@override
String get exploreMore => 'Show more';
@override
String get exploreNoDescription => 'No description.';
@override
String get exploreReadLicence => 'Read the licence';
@override
String get publishWithdraw => 'Take off Explore';
@override
String get publishWithdrawTitle => 'Take it off Explore?';
@override
String get publishWithdrawMessage =>
'Nobody can add it any more, and copies get no updates. Its releases stay as they are; publishing it again brings it back.';
@override
String get publishWithdrawn => 'Withdrawn · copies get no updates';
@override
String get publishWithdrawnDone => 'Taken off Explore.';
@override
String copyUpdateOut(int version, String summary) {
return 'v$version is out: $summary.';
}
@override
String copyUpdateChanged(int count) {
return '$count changed';
}
@override
String copyUpdateAdded(int count) {
return '$count new';
}
@override
String copyUpdateWithdrawn(int count) {
return '$count withdrawn';
}
@override
String get copyUpdateNothing => 'nothing in your copy changes';
@override
String get copyUpdate => 'Update';
@override
String copyUpdated(int version) {
return 'Updated to v$version.';
}
@override
String copyUpdatedToSettle(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count have a change of yours there too: open them to choose.',
one: 'One has a change of yours there too: open it to choose.',
);
return '$_temp0';
}
@override
String get copyUpdateFailed => 'Couldn\'t reach your instance. Try again.';
@override
String get cardBrowserStatusWithdrawn => 'Withdrawn by the author';
@override
String get settingsPrivacy => 'Privacy';
@override
String get settingsCountLearning => 'Count my learning in Explore';
@override
String get settingsCountLearningSubtitle =>
'Explore shows how many people on this instance learn a published studyset: those with an identity who chose this and studied it on 3 or more days in the last 30, rounded, from 5 on. Nothing about you is shown, and nothing leaves the instance.';
@override
String exploreLearners(int count) {
return '$count+ learn this here';
}
@override
String get publishLearnersFew =>
'Fewer than 5 learn it here, of those who chose to be counted.';
@override
String get publishForkForbidden =>
'Its original forbids passing on adaptations: only its author publishes it.';
@override
String publishLicenseMustStay(String license) {
return 'Its original asks adaptations to stay under $license: choose it under Edit, for every studyset in it.';
}
@override
String exploreBasedOn(String location) {
return 'based on $location';
}
@override
String get feedbackReport => 'Report';
@override
String get feedbackSuggestion => 'Suggestion';
@override
String get feedbackOnStudyset => 'On the studyset';
@override
String get feedbackOnCard => 'On a flashcard';
@override
String get feedbackOnNote => 'On a note';
@override
String get feedbackOnStep => 'On a step';
@override
String feedbackByOn(String name, String date) {
return '$name · $date';
}
@override
String get feedbackStatusClosed => 'Closed';
@override
String get feedbackStatusAccepted => 'Accepted';
@override
String get feedbackStatusDeclined => 'Declined';
@override
String get feedbackStatusHidden => 'Hidden';
@override
String get feedbackReplyHint => 'Write a reply…';
@override
String get feedbackSend => 'Send';
@override
String get feedbackAccept => 'Accept';
@override
String get feedbackDecline => 'Decline';
@override
String get feedbackClose => 'Close';
@override
String get feedbackReopen => 'Reopen';
@override
String get feedbackHide => 'Hide';
@override
String get feedbackShow => 'Show';
@override
String feedbackBlock(String name) {
return 'Block $name';
}
@override
String feedbackBlockTitle(String name) {
return 'Block $name?';
}
@override
String get feedbackBlockMessage =>
'What they wrote on your studysets, and write, shows to nobody but them.';
@override
String get feedbackProposed => 'Proposed';
@override
String get feedbackYours => 'Yours now';
@override
String get feedbackAuthor => 'Author';
@override
String get feedbackAccepted =>
'Taken into your studyset. The next release credits its writer.';
@override
String get feedbackDecided => 'Done.';
@override
String get feedbackReplied => 'Reply sent.';
@override
String get feedbackBlocked => 'Blocked.';
@override
String get feedbackNotTaken =>
'This can\'t be taken: what it is about isn\'t in your studyset any more.';
@override
String get feedbackFailed => 'Couldn\'t reach your instance. Try again.';
@override
String get feedbackUnreachable =>
'Reports and suggestions can\'t be reached right now.';
@override
String get feedbackRetry => 'Retry';
@override
String get inboxTitle => 'Inbox';
@override
String get inboxIntro =>
'Reports and suggestions on your published studysets, and those you wrote.';
@override
String get inboxEmpty => 'Nothing yet.';
@override
String get studysetFeedback => 'Reports and suggestions';
@override
String get studysetFeedbackEmpty =>
'Nobody has reported or suggested anything yet.';
@override
String get reportToAuthor => 'Report to the author';
@override
String get cardReportToAuthor => 'Send to the author too';
@override
String get cardReportToAuthorHint =>
'Public: everyone learning it reads it, with your public profile.';
@override
String get feedbackReportSent => 'Sent to the author.';
@override
String get suggestToAuthor => 'Suggest to the author';
@override
String get suggestHint => 'What it changes, and why';
@override
String get suggestIntro =>
'The author sees it as you have it now, beside theirs. Public: everyone learning it reads it, with your public profile.';
@override
String get suggestSent => 'Suggestion sent.';
@override
String get suggestNoDerivatives =>
'Its licence forbids adaptations: no suggestions.';
@override
String get feedbackNeedsIdentity =>
'Sign in with GitHub to report to the author and suggest.';
@override
String get kanjiExampleWords => 'Words with it';
@override
String get settingsAssistant => 'Assistant';
@override
String get settingsAskAssistant => 'Ask an AI assistant or search the web';
@override
String get settingsAskAssistantSubtitle =>
'Off unless you turn it on. Adds a field on Home and a button in the Library, on a studyset and while studying. A question opens in the AI assistant you choose or as a web search, with only the context you choose; nothing is sent from here.';
@override
String assistantHint(String assistant) {
return 'Ask $assistant…';
}
@override
String assistantHintStudyset(String assistant, String title) {
return 'Ask $assistant about $title…';
}
@override
String assistantHintCard(String assistant) {
return 'Ask $assistant about this card…';
}
@override
String assistantSend(String assistant) {
return 'Ask in $assistant';
}
@override
feat(assistant): search the web too, and choose what goes along The assistant's field now has a switch at its start: a question goes to Claude or into a web search, and the field remembers which. A web search opens the chosen engine's results: on Android the device's own web search by default (ACTION_WEB_SEARCH, answered by its default browser or search app; Brave Search where nothing answers), elsewhere Brave Search by default; Settings › Assistant › Search engine offers Ecosia, DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with %s where the question goes. Above the field, chips offer what the question is about: a card's front, back, each hint and its explanation, the studyset's name and description. None is chosen, so only what the person picks goes along, each part cut at 600 characters; Claude gets the question and a compact line per chosen part, a web search the question and the chosen text, never the studyset's name. A chosen chip alone is enough to send. The old sentences around every question (where it was asked, the connector) are gone: they cost tokens for nothing unless the studyset is chosen. On a studyset's screen the button now stands above the bottom bar, as in the Library: unfolded inside the bar, its chips lay outside the bar's bounds, where taps don't land. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
String get assistantOpen => 'Ask or search';
@override
String get assistantClose => 'Close';
@override
String assistantOpenFailed(String assistant) {
return '$assistant couldn\'t be opened.';
}
feat(assistant): Claude reads studysets through a connector on the instance A Claude plan can't drive a chat inside another app, but Claude's apps (every plan, the phone apps too) can add a remote MCP server as a custom connector. The instance now is one (ADR 0006): Serverpod's web server serves the MCP endpoint (/mcp, Streamable HTTP, JSON answers, no sessions) with five read-only tools over the person's own synced rows (their studysets, a studyset's cards and notes a page at a time, a search across cards, a note, their progress), and the OAuth the specification asks for: protected resource and authorization server metadata, dynamic client registration, authorization code with PKCE (S256 only), tokens bound to the resource, iss in the response, refresh tokens that rotate. Caddy forwards these paths alone to the web server, which production now runs on 8082. People connect with a code from the app (Settings › Assistant › Connect Claude): eight Crockford characters, valid for ten minutes and once, typed on the authorization page, which names where it returns to, loads nothing from elsewhere and may not be framed. Anonymous people can connect too. Codes and tokens are kept only as SHA-256 hashes; an access token lives an hour, a refresh token 30 days. The app lists what is connected and disconnects it, which ends its tokens at once; deleting the account deletes it; a daily job forgets what expired and clients nobody connected within a week. InstanceInfo says where the connector is (additive); the migration only adds server-only tables, so devices and older apps are untouched. The handlers take plain requests, so the integration tests drive the whole flow (registration, the code, tokens, refresh, the tools, another person's content, disconnecting, expiry, pruning, account deletion) without a web server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 12:13:00 +02:00
@override
String get assistantConnectTitle => 'Connect Claude';
@override
String get assistantConnectSubtitle => 'Let Claude read your studysets';
@override
String assistantConnectIntro(String instance) {
return 'Claude can read your studysets, cards, notes and progress on $instance through Halkyon Learn\'s connector, and answer about them. It can\'t change anything. What it reads goes to Anthropic, under their privacy policy.';
}
@override
String get assistantStepAdd =>
'In Claude, open Settings › Connectors and add a custom connector named Halkyon Learn, with this address. Added once, it works in Claude\'s apps too.';
@override
String get assistantStepCode =>
'Claude then opens a page asking for a code. Get one here: it works once, for ten minutes.';
@override
String get assistantShowCode => 'Show a code';
@override
String get assistantNewCode => 'New code';
@override
String assistantCodeValid(String time) {
return 'Valid until $time';
}
@override
String get assistantCopy => 'Copy';
@override
String get assistantCopied => 'Copied';
@override
String get assistantConnected => 'Connected';
@override
String get assistantNoneConnected => 'Nothing connected yet.';
@override
String assistantConnectedOn(String date) {
return 'connected $date';
}
@override
String assistantLastRead(String date) {
return 'last read $date';
}
@override
String get assistantDisconnect => 'Disconnect';
@override
String assistantDisconnectTitle(String name) {
return 'Disconnect $name?';
}
@override
String get assistantDisconnectMessage =>
'It can\'t read your studysets any more. You can connect it again with a new code.';
@override
String get assistantNotOffered =>
'This instance doesn\'t offer a connector yet. Questions still open in the AI assistant you chose, with what they are about.';
feat(assistant): Claude reads studysets through a connector on the instance A Claude plan can't drive a chat inside another app, but Claude's apps (every plan, the phone apps too) can add a remote MCP server as a custom connector. The instance now is one (ADR 0006): Serverpod's web server serves the MCP endpoint (/mcp, Streamable HTTP, JSON answers, no sessions) with five read-only tools over the person's own synced rows (their studysets, a studyset's cards and notes a page at a time, a search across cards, a note, their progress), and the OAuth the specification asks for: protected resource and authorization server metadata, dynamic client registration, authorization code with PKCE (S256 only), tokens bound to the resource, iss in the response, refresh tokens that rotate. Caddy forwards these paths alone to the web server, which production now runs on 8082. People connect with a code from the app (Settings › Assistant › Connect Claude): eight Crockford characters, valid for ten minutes and once, typed on the authorization page, which names where it returns to, loads nothing from elsewhere and may not be framed. Anonymous people can connect too. Codes and tokens are kept only as SHA-256 hashes; an access token lives an hour, a refresh token 30 days. The app lists what is connected and disconnects it, which ends its tokens at once; deleting the account deletes it; a daily job forgets what expired and clients nobody connected within a week. InstanceInfo says where the connector is (additive); the migration only adds server-only tables, so devices and older apps are untouched. The handlers take plain requests, so the integration tests drive the whole flow (registration, the code, tokens, refresh, the tools, another person's content, disconnecting, expiry, pruning, account deletion) without a web server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 12:13:00 +02:00
@override
String get assistantUnreachable =>
'The instance can\'t be reached. Try again once you are online.';
@override
String get assistantRetry => 'Try again';
feat(assistant): search the web too, and choose what goes along The assistant's field now has a switch at its start: a question goes to Claude or into a web search, and the field remembers which. A web search opens the chosen engine's results: on Android the device's own web search by default (ACTION_WEB_SEARCH, answered by its default browser or search app; Brave Search where nothing answers), elsewhere Brave Search by default; Settings › Assistant › Search engine offers Ecosia, DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with %s where the question goes. Above the field, chips offer what the question is about: a card's front, back, each hint and its explanation, the studyset's name and description. None is chosen, so only what the person picks goes along, each part cut at 600 characters; Claude gets the question and a compact line per chosen part, a web search the question and the chosen text, never the studyset's name. A chosen chip alone is enough to send. The old sentences around every question (where it was asked, the connector) are gone: they cost tokens for nothing unless the studyset is chosen. On a studyset's screen the button now stands above the bottom bar, as in the Library: unfolded inside the bar, its chips lay outside the bar's bounds, where taps don't land. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
@override
String assistantLineFront(String text) {
return 'Front: $text';
}
@override
String assistantLineBack(String text) {
return 'Back: $text';
}
@override
String assistantLineHint(int number, String text) {
return 'Hint $number: $text';
}
@override
String assistantLineExplanation(String text) {
return 'Explanation: $text';
}
@override
String assistantLineStudyset(String title, String instance) {
return 'Studyset: “$title” in Halkyon Learn on $instance';
}
@override
String assistantLineDescription(String text) {
return 'Description: $text';
}
@override
String assistantChipStudyset(String title) {
return 'Studyset: $title';
}
@override
String get assistantContextLabel => 'Send along';
@override
String get assistantTargetWeb => 'Web';
@override
String get assistantSwitchToWeb => 'Search the web instead';
@override
String assistantSwitchToAssistant(String assistant) {
return 'Ask $assistant instead';
}
feat(assistant): search the web too, and choose what goes along The assistant's field now has a switch at its start: a question goes to Claude or into a web search, and the field remembers which. A web search opens the chosen engine's results: on Android the device's own web search by default (ACTION_WEB_SEARCH, answered by its default browser or search app; Brave Search where nothing answers), elsewhere Brave Search by default; Settings › Assistant › Search engine offers Ecosia, DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with %s where the question goes. Above the field, chips offer what the question is about: a card's front, back, each hint and its explanation, the studyset's name and description. None is chosen, so only what the person picks goes along, each part cut at 600 characters; Claude gets the question and a compact line per chosen part, a web search the question and the chosen text, never the studyset's name. A chosen chip alone is enough to send. The old sentences around every question (where it was asked, the connector) are gone: they cost tokens for nothing unless the studyset is chosen. On a studyset's screen the button now stands above the bottom bar, as in the Library: unfolded inside the bar, its chips lay outside the bar's bounds, where taps don't land. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
@override
String get assistantHintWeb => 'Search the web…';
@override
String get assistantSendWeb => 'Search';
@override
String get assistantSearchFailed => 'The search couldn\'t be opened.';
@override
String get settingsSearchEngine => 'Search engine';
@override
String get settingsAiAssistant => 'AI assistant';
@override
String get aiAssistantCustomHint => 'https://example.org/chat?q=%s';
feat(assistant): search the web too, and choose what goes along The assistant's field now has a switch at its start: a question goes to Claude or into a web search, and the field remembers which. A web search opens the chosen engine's results: on Android the device's own web search by default (ACTION_WEB_SEARCH, answered by its default browser or search app; Brave Search where nothing answers), elsewhere Brave Search by default; Settings › Assistant › Search engine offers Ecosia, DuckDuckGo, Startpage, Qwant, Google, Bing, or one's own address with %s where the question goes. Above the field, chips offer what the question is about: a card's front, back, each hint and its explanation, the studyset's name and description. None is chosen, so only what the person picks goes along, each part cut at 600 characters; Claude gets the question and a compact line per chosen part, a web search the question and the chosen text, never the studyset's name. A chosen chip alone is enough to send. The old sentences around every question (where it was asked, the connector) are gone: they cost tokens for nothing unless the studyset is chosen. On a studyset's screen the button now stands above the bottom bar, as in the Library: unfolded inside the bar, its chips lay outside the bar's bounds, where taps don't land. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-10 14:41:36 +02:00
@override
String get searchEngineDevice => 'The device\'s search';
@override
String get searchEngineCustom => 'Your own address';
@override
String get searchEngineCustomHint => 'https://example.org/search?q=%s';
@override
String get searchEngineCustomInvalid =>
'An http or https address with %s where the question goes.';
@override
String get searchEngineCustomSave => 'Use';
2026-10-12 13:48:11 +02:00
@override
String get studysetPartMaterial => 'Material';
@override
String get studysetPartProgress => 'Progress';
@override
String get studysetPartPlan => 'Study plan';
@override
String get progressLearned => 'Learned';
@override
String get progressLearnedInfo =>
'Cards you will remember for three weeks or more.';
@override
String get progressOfStudyset => 'of this studyset';
@override
String progressCardsLearned(int learned, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$learned of $count cards',
one: '$learned of 1 card',
);
return '$_temp0';
}
@override
String get progressToReview => 'To review';
@override
String get progressToReviewInfo =>
'Cards studied before that are due again today.';
@override
String progressDueToday(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'cards due today',
one: 'card due today',
);
return '$_temp0';
}
@override
String get progressStudyTime => 'Study time';
@override
String get progressStudyTimeInfo =>
'How long you studied this studyset\'s flashcards in the last seven days, each card counted for at most a minute.';
2026-10-12 13:48:11 +02:00
@override
String get progressThisWeek => 'this week';
@override
String progressMinutes(int minutes) {
return '$minutes min';
}
@override
String progressHoursMinutes(int hours, int minutes) {
return '$hours h $minutes min';
}
@override
String planPathsDone(int done, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$done of $count paths finished',
one: '$done of 1 path finished',
);
return '$_temp0';
}
@override
String get planNoPaths => 'No learning paths yet';
@override
String get planNextUp => 'Next up';
@override
String planReview(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Study $count flashcards',
one: 'Study 1 flashcard',
);
return '$_temp0';
}
@override
String planContinue(String title) {
return 'Continue “$title”';
}
@override
String planStart(String title) {
return 'Start “$title”';
}
@override
String planStepOf(int number, int total) {
return 'Step $number of $total';
}
@override
String planStepOfTitled(int number, int total, String title) {
return 'Step $number of $total: $title';
}
@override
String planStepsDone(int done, int total) {
String _temp0 = intl.Intl.pluralLogic(
total,
locale: localeName,
other: '$done of $total steps',
one: '$done of 1 step',
);
return '$_temp0';
}
2026-10-12 13:48:11 +02:00
@override
String get planNothing => 'Nothing due right now.';
@override
String get planLearningPaths => 'Learning paths';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get examPrepare => 'Prepare for an exam';
@override
String get examPrepareHint =>
'Choose what it covers and its date, and get a pace that gets you there.';
@override
String get examChange => 'Change the exam';
@override
String examDaysLeft(int count, String date) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'In $count days, $date',
one: 'Tomorrow, $date',
zero: 'Today, $date',
);
return '$_temp0';
}
@override
String examPast(String date) {
return 'It was on $date';
}
@override
String get examPlanGone => 'This exam plan is gone';
@override
String examCardsStarted(int started, int total) {
String _temp0 = intl.Intl.pluralLogic(
total,
locale: localeName,
other: '$started of $total flashcards started',
one: '$started of 1 flashcard started',
);
return '$_temp0';
}
@override
String examPagesRead(int read, int total) {
String _temp0 = intl.Intl.pluralLogic(
total,
locale: localeName,
other: '$read of $total pages read',
one: '$read of 1 page read',
);
return '$_temp0';
}
@override
String get examPaceTitle => 'Your pace';
@override
String examPaceNew(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count new flashcards a day',
one: '1 new flashcard a day',
);
return '$_temp0';
}
@override
String examPaceSteps(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count steps a week',
one: '1 step a week',
);
return '$_temp0';
}
@override
String examPacePages(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count pages a day',
one: '1 page a day',
);
return '$_temp0';
}
@override
String get examPaceDone => 'Everything new is learnt: review until the exam.';
@override
String get examStudy => 'Study for the exam';
@override
String get examSheetHelp =>
'Choose what the exam covers and when it is. Everything new is then spread so that it is learnt a week before, leaving the last week for reviews. Only you see the date.';
@override
String get examDate => 'Exam date';
@override
String get examSheetPlans => 'This studyset’s exam plans';
@override
String get examSheetFromTags => 'From the level tags';
@override
String get examSheetNoPlans =>
'No exam plans yet. Make one from this studyset’s topics and tags.';
@override
String get examRemove => 'Stop preparing';
@override
String examPlanTopicCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count topics',
one: '1 topic',
);
return '$_temp0';
}
@override
String get examPlanNew => 'New exam plan';
@override
String get examPlanEdit => 'Edit exam plan';
@override
String get examPlanHelp =>
'An exam plan names what an exam covers: whole topics, with their flashcards, learning paths and PDFs, and flashcards with certain tags. It is published with the studyset.';
@override
String get examPlanName => 'Name';
@override
String get examPlanNameHint => 'e.g. Final exam';
@override
String get examPlanTopics => 'Topics it covers whole';
@override
String get examPlanTags => 'Flashcards with these tags';
@override
String get examPlanEmpty =>
'This studyset has no topics or tagged flashcards to choose from yet.';
@override
String get examPlanDelete => 'Delete plan';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinTitle => 'Jellyfin';
@override
String get jellyfinSubtitle =>
'Watch shows and films and listen to music from your own Jellyfin server, and learn the words of their subtitles and lyrics. Signed in, a studyset\'s Add material offers From Jellyfin.';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinPrivacy =>
'Your server\'s address and your sign-in stay on this device, and what you tie to a studyset follows you as ids, never titles. To find the words of a line, the app asks the instance about its text, which it answers and doesn\'t keep. Words you add are tagged with where you met them, kept with your flashcards like any tag.';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String jellyfinSignedInAs(String user, String server) {
return '$user on $server';
}
@override
String get jellyfinSignOut => 'Sign out of Jellyfin';
@override
String get jellyfinAddress => 'Server address';
@override
String get jellyfinAddressHint => 'jellyfin.example.org';
@override
String get jellyfinFind => 'Continue';
@override
String get jellyfinUser => 'User name';
@override
String get jellyfinPublicUsers => 'On this server';
@override
String get jellyfinPassword => 'Password';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinPasswordNone => 'Leave empty if the account has none';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinSignIn => 'Sign in';
@override
String get jellyfinQuickConnect => 'Sign in with a code';
@override
String get jellyfinQuickConnectCode =>
'Enter this code under Quick Connect in a Jellyfin app you\'re signed in to:';
@override
String get jellyfinQuickConnectWaiting =>
'Waiting for the code to be approved…';
@override
String get jellyfinOtherServer => 'Another server';
@override
String get jellyfinUnreachable =>
'No server answered at that address over HTTPS.';
@override
String get jellyfinNotEncrypted =>
'That address isn\'t encrypted, and your password would travel in the clear. Use the server\'s HTTPS address.';
@override
String get jellyfinNotJellyfin => 'That address isn\'t a Jellyfin server.';
@override
String get jellyfinWrongCredentials => 'The user name or password is wrong.';
@override
String get jellyfinSignedOut =>
'You were signed out of Jellyfin. Sign in again.';
@override
String get jellyfinFailed =>
'That didn\'t work on the Jellyfin server. Try again.';
@override
String get studysetWatch => 'From Jellyfin';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinBrowseTitle => 'Jellyfin';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinTied => 'Tied to this studyset';
@override
String get jellyfinLibraries => 'Libraries';
@override
String get jellyfinSearchHint => 'Search shows, films and music';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get jellyfinTie => 'Tie to this studyset';
@override
String get jellyfinUntie => 'Untie from this studyset';
@override
String get jellyfinEmpty => 'Nothing here';
@override
String jellyfinEpisode(int season, int episode) {
return 'S$season E$episode';
}
@override
String get jellyfinNoPlayer => 'Videos can\'t play on this device.';
@override
String get jellyfinNoPlayerLinux =>
'Videos can\'t play on this device: playing them needs libmpv (the package libmpv2).';
@override
String get watchNoSubtitles =>
'This video has no subtitles in the studyset\'s language as text, so there is nothing to look up.';
@override
String get watchSubtitles => 'Subtitles';
@override
String get watchTranslation => 'Translation';
@override
String get watchAudio => 'Audio';
@override
String get watchBack10 => '10 seconds back';
@override
String get watchForward10 => '10 seconds on';
@override
String get watchSettings => 'Settings';
@override
String get watchShortcuts => 'Keyboard shortcuts';
@override
String get watchTrackForced => 'forced';
@override
String get watchTrackCaptions => 'for the hard of hearing';
@override
String get watchKeyPlayPause => 'Play or pause';
@override
String get watchKeyFive => '5 seconds back or on';
@override
String get watchKeyTen => '10 seconds back or on';
@override
String get watchKeyLines => 'Previous or next line';
@override
String get watchKeyJump => 'To 0 %, 10 %, … 90 % of the video';
@override
String get watchKeyStartEnd => 'To the start or the end';
@override
String get watchKeyClose => 'Close the word or the settings';
@override
String get watchKeyShortcuts => 'These shortcuts';
@override
String get watchKeySpace => 'Space';
@override
String get watchKeyShift => 'Shift';
@override
String get watchKeyHome => 'Home';
@override
String get watchKeyEnd => 'End';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get watchProgressNew => 'New';
@override
String get watchProgressLearning => 'Learning';
@override
String get watchProgressDue => 'Due';
@override
String watchProgressLearned(int days) {
String _temp0 = intl.Intl.pluralLogic(
days,
locale: localeName,
other: 'again in $days days',
one: 'again tomorrow',
zero: 'again today',
);
return 'Learned · $_temp0';
}
@override
feat(dictionary): German, Greek, English, Chinese and Korean words A studyset in German, Greek, English, Chinese or Korean finds its dictionary as one in Japanese does (ADR 0008), and its subtitles are cut into words by its language (`WordCutting`): Chinese by its longest words as they stand, the longer words over each cut offered as for Japanese; Korean at its spaces, a word the dictionary lacks as it stands taken without the particle it ends with (집에서 is 집, 아이 stays a child); every other language at its spaces, a word written exactly so first (the noun Essen before the verb essen). A studyset finds the dictionary of its language tag, else of its language alone (zh for zh-Hans). A Wiktionary word shows its genders, principal forms, IPA, romanisation and Hanja; a German or Greek noun of one gender goes onto its card with its article (das Haus, το σπίτι); its tag is its word class, named in full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would read as JMdict's codes. Articles, pronouns, prepositions and conjunctions are left out of a video's words. Chinese words show their pinyin over each character on the Watch screen, carry their HSK level as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and by none for a language without. A reading is typed in a session only when it is kana, so pinyin isn't. Words are looked up on the web on Jisho in Japanese, on Wiktionary at the language's section otherwise, and a search's hint speaks of kana and romaji only in Japanese. `DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance keeps (unset: de,el,en,ko,zh; empty: none). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
String watchLookUpOn(String site) {
return 'Look up on $site';
}
@override
String get watchWider => 'Longer words here';
@override
String get watchWordsTitleSong => 'Words in this song';
@override
String get watchStudyWordsSong => 'Study this song\'s words';
@override
String get watchNoLyrics =>
'This song has no lyrics, so there is nothing to look up.';
@override
String get jellyfinNoLyrics => 'No lyrics';
@override
String get watchStudy => 'Study';
@override
String get watchStudyWords => 'Study this video\'s words';
@override
String watchAddListed(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Add $count words',
one: 'Add 1 word',
);
return '$_temp0';
}
@override
String get watchAddListedHint =>
'Adds the words listed to the studyset, tagged with where they were met, to study them';
@override
String get watchAddNothing => 'No new words listed: change the filter';
@override
String watchAddNew(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'Add the $count new words listed',
one: 'Add the 1 new word listed',
);
return '$_temp0';
}
@override
String watchAddedWords(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count words added',
one: '1 word added',
zero: 'No words added',
);
return '$_temp0';
}
@override
String get watchStudyScheduled => 'Study, on their schedules';
@override
String get watchStudyCram => 'Cram, each once';
@override
String get watchFilter => 'Filter the words';
@override
String get watchFilterFresh => 'New words';
@override
String get watchFilterHeld => 'Words in the studyset';
@override
String get watchFilterAll => 'All words';
@override
String watchFilterNoLevel(String levels) {
return 'No $levels level';
}
@override
String get watchFilterClass => 'Word class';
@override
String get watchFilterEmpty => 'No words of this video match the filter.';
@override
String get watchClassNoun => 'Nouns';
@override
String get watchClassVerb => 'Verbs';
@override
String get watchClassAdjective => 'Adjectives';
@override
String get watchClassAdverb => 'Adverbs';
@override
String get watchClassExpression => 'Expressions';
@override
String get watchClassOther => 'Others';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String get watchNone => 'None';
@override
String get watchPlay => 'Play';
@override
String get watchPause => 'Pause';
@override
String get watchPreviousLine => 'Previous line';
@override
String get watchNextLine => 'Next line';
@override
String get watchPauseAtLineEnd => 'Pause after each line';
@override
String get watchWordsTitle => 'Words in this video';
@override
String get watchWordsFinding => 'Finding the words…';
@override
String watchWordCount(int count) {
return '$count×';
}
@override
String get watchAdd => 'Add';
@override
String watchAdded(String word) {
return '$word added';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
}
@override
String watchStretchNone(String text) {
return '$text isn\'t in the dictionary as one word.';
}
@override
String get watchStretchFailed =>
'Couldn\'t look it up. Choose it again to try once more.';
@override
feat(dictionary): German, Greek, English, Chinese and Korean words A studyset in German, Greek, English, Chinese or Korean finds its dictionary as one in Japanese does (ADR 0008), and its subtitles are cut into words by its language (`WordCutting`): Chinese by its longest words as they stand, the longer words over each cut offered as for Japanese; Korean at its spaces, a word the dictionary lacks as it stands taken without the particle it ends with (집에서 is 집, 아이 stays a child); every other language at its spaces, a word written exactly so first (the noun Essen before the verb essen). A studyset finds the dictionary of its language tag, else of its language alone (zh for zh-Hans). A Wiktionary word shows its genders, principal forms, IPA, romanisation and Hanja; a German or Greek noun of one gender goes onto its card with its article (das Haus, το σπίτι); its tag is its word class, named in full (Noun, Adjective, Preposition), as Wiktionary's adj and adv would read as JMdict's codes. Articles, pronouns, prepositions and conjunctions are left out of a video's words. Chinese words show their pinyin over each character on the Watch screen, carry their HSK level as a tag (HSK-1 … HSK-7-9), and the dictionary adds a level whole; the Watch screen filters by HSK levels for Chinese, JLPT's for Japanese, and by none for a language without. A reading is typed in a session only when it is kana, so pinyin isn't. Words are looked up on the web on Jisho in Japanese, on Wiktionary at the language's section otherwise, and a search's hint speaks of kana and romaji only in Japanese. `DICTIONARY_LANGUAGES` in deploy/.env names the languages an instance keeps (unset: de,el,en,ko,zh; empty: none). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-14 07:45:15 +02:00
String watchStretchSearch(String site) {
return 'Search $site for it';
}
@override
String get watchTapAWord =>
'Tap a word of the subtitles to look it up, or drag across characters to look up any part of a line (on a touch screen, hold first).';
feat(jellyfin): watch from one's own Jellyfin server and learn the words of its subtitles Animelon's way of learning from video, from the person's own Jellyfin server: connected per device (Settings, or a studyset's Add material › Watch from Jellyfin) with a name and password or a Quick Connect code, over HTTPS only (plain HTTP only to a server on the same machine); the token stays in the system's keyring. Shows, films and videos tied to a studyset follow the person in their settings as the server's and the item's ids alone (settings schema 9), named by the server when shown. The Watch screen plays the video (video_player on Android, media_kit's libmpv on Linux and Windows) with the line in the studyset's front language cut into the dictionary's words with furigana, and the back language's line beneath; a word tapped pauses the video and becomes a card with the line and its translation as its first hint, tagged with the show. The video's words are listed, most frequent first. The sound is the track in the studyset's language, whichever the file marks default, and the menu switches it; the subtitles read are a whole track, never a forced one (which only covers what the sound doesn't say), and captions for the hard of hearing only without plain ones. The line and its translation take the video's width in a box of a fixed height, so the video keeps its size; in the dark theme the video's colours glow around it, as YouTube's ambient mode, and the words' panel takes the colour of the episode's picture under the pointer. A line is cut into words with one request: DictionaryEndpoint.withKeys looks up every stretch of it that may be a word, as it stands and as every word it may be a conjugation of, in the new dictionary_key table (one row per key, indexed; migration dictionary-keys adds the table and its index, nothing else), which the loader fills beside the entries. The instance gets the text of each line looked up and keeps none of it, as the Watch screen says. Linux builds need libmpv-dev, and the app links libmpv: the snap stages Ubuntu's libmpv2, makes the update-alternatives links for BLAS and LAPACK that ffmpeg's speech recognition needs (staging never runs install scripts), and removes what the GNOME runtime and core24 already hold, as older copies of cairo, pango and librsvg hid the runtime's from its GTK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 11:31:37 +02:00
@override
String watchFormOf(String word) {
return 'A form of $word';
}
@override
String get watchFailed => 'The video couldn\'t be played.';
feat(app): the UI foundation: theme, tabs and routes, strings The app gets its look and navigation before more features land on them. - Theme (core/theme/): hand-written light and dark Material 3 colour schemes around cobalt and copper; ThemeExtras for the colours Material has no role for (states, the four FSRS ratings); the 20 colours a person can give content. Tests hold text to WCAG AA contrast on each palette colour and on the dark theme's primary colour and surface. - Layout and shared widgets: Material 3's window-size classes (core/layout/); SectionCard, AppLoadingIndicator, confirmDestructiveAction and FoundById (core/widget/). Lucide icons. - Navigation with go_router: four tabs (Home, Library, Explore, Profile), each keeping its own stack of screens, as a navigation rail with the app's icon from 600 dp and as a navigation bar below; choosing the open tab again returns to its first screen. A studyset's screen sits in the library; studying, cards and settings cover the tabs. - Routes carry ids: FoundById finds the item in the repository's live list, so a studyset deleted on another device, or an old link, says it no longer exists. A screen that deletes its own item keeps showing it while it closes, rather than flashing that message. - Every person gets an app of their own (PersonApp, lib/src/app/): a MaterialApp.router with its own GoRouter and PersonScope around the navigator, built by SessionGate inside the person's keyed subtree. Screens read the person's repositories wherever they are built, so the static route() helpers and the listener that closed every pushed screen on a change of person are gone: a change of person now disposes the whole router and starts again at home. - The account dialog becomes the Profile tab, with Settings (sign out, delete the account, About) behind a gear. A waiting GitHub sign-in now keeps waiting while another tab shows; a change of person still cancels it. - The new screens' strings come from Flutter's gen-l10n (English for now). The generated files are committed, left out of analysis and formatting, and written anew by `flutter pub get`, so CI's check of generated code also catches stale ones. Widget tests now start the whole app (test/support/pump_app.dart) at the screen's path and move through it as a person would, by tabs and taps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-01 00:18:31 +02:00
}
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
/// The translations for English, as used in the United Kingdom (`en_GB`).
class AppLocalizationsEnGb extends AppLocalizationsEn {
AppLocalizationsEnGb() : super('en_GB');
}
/// The translations for English, as used in the United States (`en_US`).
class AppLocalizationsEnUs extends AppLocalizationsEn {
AppLocalizationsEnUs() : super('en_US');
@override
String get studysetSheetColor => 'Color';
@override
String colorPickerLabel(String color) {
return 'Color: $color';
}
@override
String get studysetSheetLicense => 'License';
@override
String get studysetSheetLicenseLabel => 'License';
@override
String get studysetSheetLicenseHolder => 'License holder';
@override
String get studysetSheetLicenseHolderHelper =>
'Who to credit: a name or an organization.';
@override
String studysetSheetLicenseNewer(String license, String current) {
return '$license is an older Creative Commons license. Its current version, $current, may fit better.';
}
@override
String get licenseChooserTitle => 'Choose a license';
@override
String get licenseChooserSubtitle =>
'A few questions to find the Creative Commons license that fits how you want your work shared.';
@override
String get licenseChooserIntroYes => 'Yes, share it under a license';
@override
String get licenseChooserAdaptationsShareAlikeDetail =>
'Adaptations carry this same license (SA).';
@override
String get licenseChooserRecommended => 'Recommended license';
@override
String get licenseChooserUse => 'Use this license';
@override
String get licenseSummaryBySa =>
'Others may share and adapt it, even commercially, if they credit you and license their adaptations the same way.';
@override
String get licenseSummaryByNcSa =>
'Others may share and adapt it non-commercially, with credit and the same license on adaptations.';
@override
String get licenseSummaryByNcNd =>
'Others may share it non-commercially, with credit, but may not adapt it: the strictest Creative Commons license.';
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
@override
String get tagSheetColor => 'Color';
@override
String get formatAlignCenter => 'Center';
feat(app): Home, the Library and the studyset sheets laid out as before Home: plain cards on the surface, 16 dp apart, the goal first and the Pomodoro timer after it in a new dashboard. The streak chip sits centred in the app bar, and its sheet shows the days inside a big flame and the week as marks over the weekdays' initials, with a word on today in a box. The week names its day as "Thursday, Oct 8" and its days carry no tooltip. The heatmap scrolls its weekdays with the weeks, outlines the days to come, and names a day's activities with its date. The goal card has a ring of 5 dp, a check once met and a pencil, and its sheet saves across its width with Remove goal below. Arranging puts an eye on each widget's corner and lifts the one picked up, with a tap the hand feels; with every widget hidden, Customise brings arranging back. The stepper moves to core, where the goal's sheet takes it too. Library: the counts line includes files (PDFs); Refresh in the app bar and pulling the list down sync now; a list that can't be read offers Retry, which reads it again; the star keeps its square corners. Studyset sheet: "Title *", the secondary title explained behind a question mark, Inherit as a box beside the colour, and the description in up to three lines with a chevron. Move sheet: round marks, the studyset itself marked as current, the top level only while it is inside another, archived studysets last under their heading, the destination named in the footer, and a new studyset to move into, made only once the move is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-08 11:12:23 +02:00
@override
String get homeCustomise => 'Customize';
@override
String get publishLicence => 'License';
@override
String publishLicenceNotOpen(String titles) {
return 'These studysets\' licenses don\'t allow passing them on: $titles';
}
@override
String get exploreReadLicence => 'Read the license';
@override
String get suggestNoDerivatives =>
'Its license forbids adaptations: no suggestions.';
feat(app): the app speaks British and American English and German Settings offers English (UK), English (US) and Deutsch after the device's language, each named in its own language. app_en.arb, the template, is now British English (colour, centre) and the fallback for any language the app doesn't speak; app_en_US.arb overrides the five messages American English spells differently, and app_en_GB.arb is empty, there so that Material's texts and dates are British too. The German translation says "du" and keeps one set of terms throughout (Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in the Anki import). arb_files_test checks that German has every message of the template and that every translation keeps its placeholders. resolveAppLocale picks the first language the device prefers that the app speaks, in its country's variant where there is one; other English is British, and a country alone never picks a language (Spanish in the United States isn't American English), which Flutter's own resolution would do. Both MaterialApps take material_ui's GlobalMaterialLocalizations delegates: gen-l10n's list brings flutter_localizations', which serve only package:flutter/material.dart, so material_ui's widgets found no Material texts in German and threw. The editor's own text blocks show our placeholders while empty (list item, to-do, quote, heading, and the desktop's / hint): the editor's own follow a locale of their own, process-wide, in few languages. The last texts written into widgets (the person's data failing to open, the cards failing to read, the About dialog) moved into the arb files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-07 02:01:01 +02:00
}