133 lines
5.5 KiB
Dart
133 lines
5.5 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/app/app_controller.dart';
|
|
import 'package:jj_mkvmaker/app/i18n_controller.dart';
|
|
import 'package:jj_mkvmaker/app/settings.dart';
|
|
import 'package:jj_mkvmaker/core/languages.dart';
|
|
import 'package:jj_mkvmaker/core/playlist.dart';
|
|
import 'package:jj_mkvmaker/l10n/tr.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/desktop_storage_service.dart';
|
|
import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
|
import 'package:jj_mkvmaker/services/ai_services.dart';
|
|
import 'package:jj_mkvmaker/services/model_store.dart';
|
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
|
import 'package:jj_mkvmaker/ui/home_page.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// 설치된 것처럼 보이는 모델 저장소 (내려받지 않음)
|
|
class _Models extends ModelStore {
|
|
_Models(super.dir);
|
|
@override
|
|
Future<bool> isInstalled(ModelSpec m) async => true;
|
|
}
|
|
|
|
/// 가짜 번역기: "[대상] 원문" (자리 표시는 그대로)
|
|
class _Translator implements Translator {
|
|
@override
|
|
Future<void> load(String modelDir) async {}
|
|
@override
|
|
Future<List<String>> translate(List<String> lines,
|
|
{required String source, required String target, AiProgress? onProgress}) async =>
|
|
[for (final l in lines) l.startsWith('Make') ? 'Faire' : '[$target] $l'];
|
|
@override
|
|
void cancel() {}
|
|
@override
|
|
Future<void> dispose() async {}
|
|
}
|
|
|
|
Map<String, String> _asset(String code) =>
|
|
Map<String, String>.from(jsonDecode(File('assets/l10n/$code.json').readAsStringSync()) as Map);
|
|
|
|
void main() {
|
|
late Directory dir;
|
|
late AppController c;
|
|
setUp(() {
|
|
dir = Directory.systemTemp.createTempSync('jj_i18n_');
|
|
c = AppController(PlatformServices(
|
|
mediaTool: ProcessMediaTool('x', 'y'),
|
|
storage: DesktopStorageService(),
|
|
createTranslator: _Translator.new,
|
|
models: _Models(dir.path),
|
|
));
|
|
i18n
|
|
..init(c, dir.path)
|
|
..loadAsset = (path) async => File(path).readAsString();
|
|
});
|
|
tearDown(() async {
|
|
setTranslations('ko', const {});
|
|
dir.deleteSync(recursive: true);
|
|
});
|
|
|
|
test('기본 사전: 영어 · 일본어 · 중국어가 같은 열쇠를 모두 가진다', () {
|
|
final en = _asset('en'), ja = _asset('ja'), zh = _asset('zh-Hans');
|
|
expect(en.length, greaterThan(700));
|
|
expect(ja.keys.toSet(), en.keys.toSet());
|
|
expect(zh.keys.toSet(), en.keys.toSet());
|
|
expect(en['MKV 만들기'], 'Make MKV');
|
|
expect(ja['환경 설정'], '環境設定');
|
|
expect(zh['취소'], '取消');
|
|
});
|
|
|
|
test('언어 바꾸기: tr · trf · 선택 목록 이름 · 언어 이름이 그 언어로, 한국어로 되돌리기', () async {
|
|
expect(tr('MKV 만들기'), 'MKV 만들기');
|
|
await i18n.apply('en');
|
|
expect(c.settings.uiLanguage, 'en');
|
|
expect(tr('MKV 만들기'), 'Make MKV');
|
|
expect(trf('동영상 {0}개 · 전체 선택', [3]), '3 videos · select all');
|
|
expect(PlaylistMode.folder.label, 'All videos in the same folder');
|
|
expect(languageOf('ja').name, 'Japanese');
|
|
expect(tr('사전에 없는 글자'), '사전에 없는 글자'); // 없으면 원문
|
|
await i18n.apply('ja');
|
|
expect(tr('환경 설정'), '環境設定');
|
|
await i18n.apply('ko');
|
|
expect(tr('환경 설정'), '환경 설정');
|
|
expect(AppSettings.fromJson(c.settings.toJson()).uiLanguage, 'ko');
|
|
});
|
|
|
|
test('사전 번역: 여러 줄은 줄마다, 자리 표시가 사라지면 영어 그대로', () async {
|
|
final out = await translateDictionary(
|
|
{'a': 'Line one\nLine two', 'b': 'Make {0} MKVs', 'c': '', 'd': '{0} videos'},
|
|
(lines, onProgress) async => [for (final l in lines) l.startsWith('Make') ? 'Faire' : 'X:$l'],
|
|
);
|
|
expect(out['a'], 'X:Line one\nX:Line two');
|
|
expect(out['b'], 'Make {0} MKVs'); // {0} 이 사라져서 영어 그대로
|
|
expect(out['c'], '');
|
|
expect(out['d'], 'X:{0} videos');
|
|
});
|
|
|
|
test('언어 추가 (AI 번역) → 고르기 → 지우기', () async {
|
|
final fr = languageOf('fr');
|
|
expect(i18n.addable.map((l) => l.code), contains('fr'));
|
|
expect(i18n.addable.map((l) => l.code), isNot(contains('en')));
|
|
await i18n.addLanguage(fr);
|
|
expect(c.settings.uiLanguagesAdded, ['fr']);
|
|
expect(i18n.available, ['ko', 'en', 'ja', 'zh-Hans', 'fr']);
|
|
expect(File(p.join(dir.path, 'l10n', 'fr.json')).existsSync(), isTrue);
|
|
await i18n.apply('fr');
|
|
expect(tr('취소'), '[fra_Latn] Cancel');
|
|
expect(trf('{0}개 선택', [2]), '[fra_Latn] 2 selected');
|
|
await i18n.removeLanguage('fr');
|
|
expect(c.settings.uiLanguagesAdded, isEmpty);
|
|
expect(uiLanguage, 'ko', reason: '쓰던 언어를 지우면 한국어로');
|
|
expect(File(p.join(dir.path, 'l10n', 'fr.json')).existsSync(), isFalse);
|
|
});
|
|
|
|
testWidgets('화면 언어를 바꾸면 열려 있는 화면이 바로 그 언어로 다시 그려진다', (tester) async {
|
|
tester.view.physicalSize = const Size(1600, 900);
|
|
tester.view.devicePixelRatio = 1;
|
|
addTearDown(tester.view.reset);
|
|
await tester.pumpWidget(MaterialApp(home: HomePage(c: c, onExit: () {})));
|
|
expect(find.text('동영상 추가'), findsWidgets);
|
|
await tester.runAsync(() => i18n.apply('en'));
|
|
await tester.pump();
|
|
expect(find.text('Add videos'), findsWidgets);
|
|
expect(find.text('동영상 추가'), findsNothing);
|
|
await tester.runAsync(() => i18n.apply('zh-Hans'));
|
|
await tester.pump();
|
|
expect(find.text('添加视频'), findsWidgets);
|
|
});
|
|
}
|