fix(study): a Japanese word's romaji reading is not typed as pinyin

Any reading in Latin letters counted as pinyin, whatever the studyset's
language, so a Japanese word read in romaji (東京 as Tōkyō or toukyou,
common in imported decks), which was never typed for having no kana, was
typed with pinyin's tone numbers and hint, and tokyo was marked wrong.

A reading is now pinyin only when the studied studyset's fronts are in
Chinese (zh, in any script or region), its own language or inherited: the
session reads it as each round starts, the card the same languages its
screen is set in. A Japanese studyset studies as it did before pinyin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-14 17:48:15 +02:00
commit fd783df649
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 80 additions and 24 deletions

View file

@ -39,21 +39,30 @@ void main() {
});
test("pinyin is told from kana and from words' own letters", () {
expect(isPinyin('xué xí'), isTrue);
expect(isPinyin('shuō'), isTrue);
expect(isPinyin('たべる'), isFalse);
expect(isPinyin('가치'), isFalse);
expect(isPinyin(''), isFalse);
expect(isPinyin('xué xí', language: 'zh'), isTrue);
expect(isPinyin('shuō', language: 'zh'), isTrue);
expect(isPinyin('たべる', language: 'zh'), isFalse);
expect(isPinyin('가치', language: 'zh'), isFalse);
expect(isPinyin('', language: 'zh'), isFalse);
});
test('a proper noun, capitalised with its tone mark, is pinyin too', () {
expect(isPinyin('Ōu zhōu'), isTrue);
expect(isPinyin('Ào mén'), isTrue);
expect(isPinyin('É yǔ'), isTrue);
expect(isPinyin('Lǘ'), isTrue);
expect(isPinyin('Ōu zhōu', language: 'zh'), isTrue);
expect(isPinyin('Ào mén', language: 'zh'), isTrue);
expect(isPinyin('É yǔ', language: 'zh'), isTrue);
expect(isPinyin('Lǘ', language: 'zh'), isTrue);
expect(givenPinyin('Ōu zhōu').contains(pinyinKey('ou1zhou1')), isTrue);
});
test("only a Chinese word's reading is pinyin, in any script or region; "
"a Japanese word's romaji is not", () {
expect(isPinyin('xué xí', language: 'zh-Hans'), isTrue);
expect(isPinyin('xué xí', language: 'zh-Hant-TW'), isTrue);
expect(isPinyin('Tōkyō', language: 'ja'), isFalse);
expect(isPinyin('toukyou', language: 'ja'), isFalse);
expect(isPinyin('xué xí', language: null), isFalse);
});
test('typing turns a syllable and its tone into the marked syllable, the '
'caret where it was typed', () {
const formatter = PinyinInputFormatter();

View file

@ -72,12 +72,14 @@ void main() {
});
/// Studies the studyset `japanese`, with the cards the test gave [cards]:
/// the app reads them from the same [gateway].
/// the app reads them from the same [gateway]. Its fronts are in
/// [frontLanguage], none unless given.
Future<void> study(
WidgetTester tester, {
GeoMapRepository? maps,
FakeReviewGateway? reviews,
StrokeRepository? strokes,
String? frontLanguage,
double width = 1200,
}) async {
tester.view.physicalSize = Size(width, 1600);
@ -89,7 +91,13 @@ void main() {
studysetGateway: FakeStudysetGateway()
..receiveRemoteChange(
'japanese',
encodeStudysetPayload(StudysetContent.titled('Japanese')),
encodeStudysetPayload(
StudysetContent(
title: 'Japanese',
description: RichContent.empty(),
frontLanguage: frontLanguage,
),
),
),
cardGateway: gateway,
tagGateway: tagGateway,
@ -893,7 +901,7 @@ void main() {
await settings.update(
(each) => each.withStudy(const StudyOptions(typeReadings: true)),
);
await study(tester);
await study(tester, frontLanguage: 'zh-Hans');
expect(find.text('The pinyin, with tone numbers or marks'), findsOneWidget);
await tester.enterText(find.byType(TextField), 'xue2xi2');
@ -905,6 +913,29 @@ void main() {
expect(find.text('Right'), findsOneWidget);
});
testWidgets('typing readings, a Japanese word read in romaji flips as '
"before: they aren't pinyin", (tester) async {
await cards.create(
'japanese',
CardContent(
type: CardType.vocabulary,
front: paragraph('東京'),
back: RichContent.empty(),
reading: 'Tōkyō',
meanings: const ['Tokyo'],
),
);
final settings = SettingsRepository(settingsGateway);
addTearDown(settings.dispose);
await settings.update(
(each) => each.withStudy(const StudyOptions(typeReadings: true)),
);
await study(tester, frontLanguage: 'ja');
expect(find.byType(TextField), findsNothing);
expect(find.text('Show answer'), findsOneWidget);
});
testWidgets('a reading typed right says so', (tester) async {
await cards.create(
'japanese',