원어를 자동 감지했으면 끝난 뒤 감지한 언어 · [다른 언어로 다시] (198)
- 사용자 편의 (기기 확인): 영어 음성을 한국어로 감지해 영어 발음을 한글로 적은 자막 - 막지는 못하므로 알리고 고른 원어로 다시 만들 수 있게 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>묶음5
parent
65c049d043
commit
36bbee615d
|
|
@ -59,6 +59,7 @@
|
||||||
"(현재 방식은 옵션 없음)": "(no options for the built-in method)",
|
"(현재 방식은 옵션 없음)": "(no options for the built-in method)",
|
||||||
"* 는 아무 글자, ? 는 한 글자. 여러 개는 ; 로 (예: *.srt; *.ass)": "* matches any text, ? one character. Separate several with ; (e.g. *.srt; *.ass)",
|
"* 는 아무 글자, ? 는 한 글자. 여러 개는 ; 로 (예: *.srt; *.ass)": "* matches any text, ? one character. Separate several with ; (e.g. *.srt; *.ass)",
|
||||||
"+ 여기에 새 줄": "+ New line here",
|
"+ 여기에 새 줄": "+ New line here",
|
||||||
|
"- 감지한 언어": "- detected",
|
||||||
"-1 은 아무렇게나": "-1 = random",
|
"-1 은 아무렇게나": "-1 = random",
|
||||||
".{0} 재생 프로그램 (exe)": ".{0} player (exe)",
|
".{0} 재생 프로그램 (exe)": ".{0} player (exe)",
|
||||||
"0 = 제한 없음. 모든 방법에 적용: rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게. 옵션 칸에 직접 넣어도 됩니다 (그쪽이 우선)": "0 = unlimited. Applies to every method: rsync --bwlimit, the built-in method throttles itself, robocopy approximates with /IPG. You can also put it in the options field (that takes priority)",
|
"0 = 제한 없음. 모든 방법에 적용: rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게. 옵션 칸에 직접 넣어도 됩니다 (그쪽이 우선)": "0 = unlimited. Applies to every method: rsync --bwlimit, the built-in method throttles itself, robocopy approximates with /IPG. You can also put it in the options field (that takes priority)",
|
||||||
|
|
@ -442,6 +443,7 @@
|
||||||
"가져온 즐겨찾기": "Imported bookmarks",
|
"가져온 즐겨찾기": "Imported bookmarks",
|
||||||
"가져온 즐겨찾기 ({0})": "Imported bookmarks ({0})",
|
"가져온 즐겨찾기 ({0})": "Imported bookmarks ({0})",
|
||||||
"가져올 즐겨찾기가 없습니다.": "No bookmarks to import.",
|
"가져올 즐겨찾기가 없습니다.": "No bookmarks to import.",
|
||||||
|
"감지한 언어: {0} - 틀렸으면 다른 언어로 다시 만드세요": "Detected language: {0} - if it is wrong, make them again with another language",
|
||||||
"같은 시리즈 (번호만 다른 파일)": "Same series (files that differ only by number)",
|
"같은 시리즈 (번호만 다른 파일)": "Same series (files that differ only by number)",
|
||||||
"같은 이름이 이미 있습니다": "An item with the same name already exists",
|
"같은 이름이 이미 있습니다": "An item with the same name already exists",
|
||||||
"같은 이름이 이미 있습니다: {0}": "Already exists with the same name: {0}",
|
"같은 이름이 이미 있습니다: {0}": "Already exists with the same name: {0}",
|
||||||
|
|
@ -641,6 +643,7 @@
|
||||||
"다른 앱에서 동영상을 열 때": "When opening a video from another app",
|
"다른 앱에서 동영상을 열 때": "When opening a video from another app",
|
||||||
"다른 앱에서 열기: {0}": "Opened from another app: {0}",
|
"다른 앱에서 열기: {0}": "Opened from another app: {0}",
|
||||||
"다른 앱으로 열기": "Open with another app",
|
"다른 앱으로 열기": "Open with another app",
|
||||||
|
"다른 언어로 다시": "Redo with another language",
|
||||||
"다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. 주소창 옆 번역 버튼으로 원문 · 번역을 바꿀 수 있습니다.": "Translates pages in other languages into the display language ({0}). The page text is sent to Google Translate. Use the translate button next to the address bar to switch between original · translation.",
|
"다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. 주소창 옆 번역 버튼으로 원문 · 번역을 바꿀 수 있습니다.": "Translates pages in other languages into the display language ({0}). The page text is sent to Google Translate. Use the translate button next to the address bar to switch between original · translation.",
|
||||||
"다른 언어로 번역 (AI · 이 {0}에서)": "Translate to another language (AI · on this {0})",
|
"다른 언어로 번역 (AI · 이 {0}에서)": "Translate to another language (AI · on this {0})",
|
||||||
"다른 이름으로 저장": "Save as",
|
"다른 이름으로 저장": "Save as",
|
||||||
|
|
@ -1415,6 +1418,7 @@
|
||||||
"원어({0})와 같은 언어로는 번역하지 않습니다.": "Will not translate into the same language as the source ({0}).",
|
"원어({0})와 같은 언어로는 번역하지 않습니다.": "Will not translate into the same language as the source ({0}).",
|
||||||
"원어: {0} ({1})": "Source: {0} ({1})",
|
"원어: {0} ({1})": "Source: {0} ({1})",
|
||||||
"원어: 자동 감지 (글자로 추정)": "Source: auto-detect (guessed from the text)",
|
"원어: 자동 감지 (글자로 추정)": "Source: auto-detect (guessed from the text)",
|
||||||
|
"원어를 골라 다시 만들기": "Choose the source language and redo",
|
||||||
"원인: {0}": "Cause: {0}",
|
"원인: {0}": "Cause: {0}",
|
||||||
"월": "Mon",
|
"월": "Mon",
|
||||||
"웹 브라우저": "Web browser",
|
"웹 브라우저": "Web browser",
|
||||||
|
|
@ -1849,6 +1853,7 @@
|
||||||
"처음 쓸 때 내려받기 (기본)": "Download on first use (default)",
|
"처음 쓸 때 내려받기 (기본)": "Download on first use (default)",
|
||||||
"처음 아이콘 (파랑)": "Original icon (blue)",
|
"처음 아이콘 (파랑)": "Original icon (blue)",
|
||||||
"처음대로": "Reset to default",
|
"처음대로": "Reset to default",
|
||||||
|
"처음부터 정하려면 AI 자막 창의 \"원어 (영상 속 언어)\" 에서 고르세요.": "To set it from the start, choose it in \"Source language (spoken in the video)\" in the AI subtitles window.",
|
||||||
"처음으로: {0}": "Back to the first: {0}",
|
"처음으로: {0}": "Back to the first: {0}",
|
||||||
"첫 장입니다.": "This is the first page.",
|
"첫 장입니다.": "This is the first page.",
|
||||||
"첫 줄 추가": "Add first line",
|
"첫 줄 추가": "Add first line",
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@
|
||||||
"(현재 방식은 옵션 없음)": "(現在の方式にはオプションなし)",
|
"(현재 방식은 옵션 없음)": "(現在の方式にはオプションなし)",
|
||||||
"* 는 아무 글자, ? 는 한 글자. 여러 개는 ; 로 (예: *.srt; *.ass)": "* は任意の文字列、? は 1 文字。複数は ; で区切る (例: *.srt; *.ass)",
|
"* 는 아무 글자, ? 는 한 글자. 여러 개는 ; 로 (예: *.srt; *.ass)": "* は任意の文字列、? は 1 文字。複数は ; で区切る (例: *.srt; *.ass)",
|
||||||
"+ 여기에 새 줄": "+ ここに新しい行",
|
"+ 여기에 새 줄": "+ ここに新しい行",
|
||||||
|
"- 감지한 언어": "- 検出した言語",
|
||||||
"-1 은 아무렇게나": "-1 はランダム",
|
"-1 은 아무렇게나": "-1 はランダム",
|
||||||
".{0} 재생 프로그램 (exe)": ".{0} 再生プログラム(exe)",
|
".{0} 재생 프로그램 (exe)": ".{0} 再生プログラム(exe)",
|
||||||
"0 = 제한 없음. 모든 방법에 적용: rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게. 옵션 칸에 직접 넣어도 됩니다 (그쪽이 우선)": "0 = 制限なし。すべての方法に適用: rsync は --bwlimit、現在の方式はアプリが調整、robocopy は /IPG で近似。オプション欄に直接入れても構いません (そちらが優先)",
|
"0 = 제한 없음. 모든 방법에 적용: rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게. 옵션 칸에 직접 넣어도 됩니다 (그쪽이 우선)": "0 = 制限なし。すべての方法に適用: rsync は --bwlimit、現在の方式はアプリが調整、robocopy は /IPG で近似。オプション欄に直接入れても構いません (そちらが優先)",
|
||||||
|
|
@ -442,6 +443,7 @@
|
||||||
"가져온 즐겨찾기": "取り込んだブックマーク",
|
"가져온 즐겨찾기": "取り込んだブックマーク",
|
||||||
"가져온 즐겨찾기 ({0})": "取り込んだブックマーク({0})",
|
"가져온 즐겨찾기 ({0})": "取り込んだブックマーク({0})",
|
||||||
"가져올 즐겨찾기가 없습니다.": "取り込むブックマークがありません。",
|
"가져올 즐겨찾기가 없습니다.": "取り込むブックマークがありません。",
|
||||||
|
"감지한 언어: {0} - 틀렸으면 다른 언어로 다시 만드세요": "検出した言語: {0} - 違っていれば別の言語で作り直してください",
|
||||||
"같은 시리즈 (번호만 다른 파일)": "同じシリーズ(番号だけ違うファイル)",
|
"같은 시리즈 (번호만 다른 파일)": "同じシリーズ(番号だけ違うファイル)",
|
||||||
"같은 이름이 이미 있습니다": "同じ名前のものが既にあります",
|
"같은 이름이 이미 있습니다": "同じ名前のものが既にあります",
|
||||||
"같은 이름이 이미 있습니다: {0}": "同じ名前が既にあります: {0}",
|
"같은 이름이 이미 있습니다: {0}": "同じ名前が既にあります: {0}",
|
||||||
|
|
@ -641,6 +643,7 @@
|
||||||
"다른 앱에서 동영상을 열 때": "他のアプリで動画を開いたとき",
|
"다른 앱에서 동영상을 열 때": "他のアプリで動画を開いたとき",
|
||||||
"다른 앱에서 열기: {0}": "他のアプリから開く: {0}",
|
"다른 앱에서 열기: {0}": "他のアプリから開く: {0}",
|
||||||
"다른 앱으로 열기": "別のアプリで開く",
|
"다른 앱으로 열기": "別のアプリで開く",
|
||||||
|
"다른 언어로 다시": "別の言語でやり直す",
|
||||||
"다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. 주소창 옆 번역 버튼으로 원문 · 번역을 바꿀 수 있습니다.": "ほかの言語のページを表示言語 ({0}) に翻訳します。ページの文章は Google 翻訳に送られます。アドレスバー横の翻訳ボタンで原文 · 翻訳を切り替えられます。",
|
"다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. 주소창 옆 번역 버튼으로 원문 · 번역을 바꿀 수 있습니다.": "ほかの言語のページを表示言語 ({0}) に翻訳します。ページの文章は Google 翻訳に送られます。アドレスバー横の翻訳ボタンで原文 · 翻訳を切り替えられます。",
|
||||||
"다른 언어로 번역 (AI · 이 {0}에서)": "他の言語に翻訳(AI・この{0}で)",
|
"다른 언어로 번역 (AI · 이 {0}에서)": "他の言語に翻訳(AI・この{0}で)",
|
||||||
"다른 이름으로 저장": "名前を付けて保存",
|
"다른 이름으로 저장": "名前を付けて保存",
|
||||||
|
|
@ -1415,6 +1418,7 @@
|
||||||
"원어({0})와 같은 언어로는 번역하지 않습니다.": "原語({0})と同じ言語には翻訳しません。",
|
"원어({0})와 같은 언어로는 번역하지 않습니다.": "原語({0})と同じ言語には翻訳しません。",
|
||||||
"원어: {0} ({1})": "原語: {0}({1})",
|
"원어: {0} ({1})": "原語: {0}({1})",
|
||||||
"원어: 자동 감지 (글자로 추정)": "原語: 自動検出(文字から推定)",
|
"원어: 자동 감지 (글자로 추정)": "原語: 自動検出(文字から推定)",
|
||||||
|
"원어를 골라 다시 만들기": "原語を選んで作り直す",
|
||||||
"원인: {0}": "原因: {0}",
|
"원인: {0}": "原因: {0}",
|
||||||
"월": "月",
|
"월": "月",
|
||||||
"웹 브라우저": "ウェブブラウザー",
|
"웹 브라우저": "ウェブブラウザー",
|
||||||
|
|
@ -1849,6 +1853,7 @@
|
||||||
"처음 쓸 때 내려받기 (기본)": "初めて使うときにダウンロード (既定)",
|
"처음 쓸 때 내려받기 (기본)": "初めて使うときにダウンロード (既定)",
|
||||||
"처음 아이콘 (파랑)": "最初のアイコン(青)",
|
"처음 아이콘 (파랑)": "最初のアイコン(青)",
|
||||||
"처음대로": "既定に戻す",
|
"처음대로": "既定に戻す",
|
||||||
|
"처음부터 정하려면 AI 자막 창의 \"원어 (영상 속 언어)\" 에서 고르세요.": "最初から決めるには、AI 字幕ウィンドウの「原語(動画内の言語)」で選んでください。",
|
||||||
"처음으로: {0}": "最初へ: {0}",
|
"처음으로: {0}": "最初へ: {0}",
|
||||||
"첫 장입니다.": "最初のページです。",
|
"첫 장입니다.": "最初のページです。",
|
||||||
"첫 줄 추가": "最初の行を追加",
|
"첫 줄 추가": "最初の行を追加",
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@
|
||||||
"(현재 방식은 옵션 없음)": "(当前方式没有选项)",
|
"(현재 방식은 옵션 없음)": "(当前方式没有选项)",
|
||||||
"* 는 아무 글자, ? 는 한 글자. 여러 개는 ; 로 (예: *.srt; *.ass)": "* 表示任意字符,? 表示一个字符。多个用 ; 分隔(例:*.srt; *.ass)",
|
"* 는 아무 글자, ? 는 한 글자. 여러 개는 ; 로 (예: *.srt; *.ass)": "* 表示任意字符,? 表示一个字符。多个用 ; 分隔(例:*.srt; *.ass)",
|
||||||
"+ 여기에 새 줄": "+ 在此新增一行",
|
"+ 여기에 새 줄": "+ 在此新增一行",
|
||||||
|
"- 감지한 언어": "- 检测到的语言",
|
||||||
"-1 은 아무렇게나": "-1 为随机",
|
"-1 은 아무렇게나": "-1 为随机",
|
||||||
".{0} 재생 프로그램 (exe)": ".{0} 播放程序(exe)",
|
".{0} 재생 프로그램 (exe)": ".{0} 播放程序(exe)",
|
||||||
"0 = 제한 없음. 모든 방법에 적용: rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게. 옵션 칸에 직접 넣어도 됩니다 (그쪽이 우선)": "0 = 不限制。适用于所有方式: rsync 用 --bwlimit,当前方式由应用调节,robocopy 用 /IPG 近似。也可直接写在选项栏中 (优先)",
|
"0 = 제한 없음. 모든 방법에 적용: rsync --bwlimit, 현재 방식은 앱이 조절, robocopy 는 /IPG 로 비슷하게. 옵션 칸에 직접 넣어도 됩니다 (그쪽이 우선)": "0 = 不限制。适用于所有方式: rsync 用 --bwlimit,当前方式由应用调节,robocopy 用 /IPG 近似。也可直接写在选项栏中 (优先)",
|
||||||
|
|
@ -442,6 +443,7 @@
|
||||||
"가져온 즐겨찾기": "导入的书签",
|
"가져온 즐겨찾기": "导入的书签",
|
||||||
"가져온 즐겨찾기 ({0})": "导入的书签({0})",
|
"가져온 즐겨찾기 ({0})": "导入的书签({0})",
|
||||||
"가져올 즐겨찾기가 없습니다.": "没有可导入的书签。",
|
"가져올 즐겨찾기가 없습니다.": "没有可导入的书签。",
|
||||||
|
"감지한 언어: {0} - 틀렸으면 다른 언어로 다시 만드세요": "检测到的语言:{0} - 如有误,请用其他语言重新生成",
|
||||||
"같은 시리즈 (번호만 다른 파일)": "同一系列(仅编号不同的文件)",
|
"같은 시리즈 (번호만 다른 파일)": "同一系列(仅编号不同的文件)",
|
||||||
"같은 이름이 이미 있습니다": "已存在同名项目",
|
"같은 이름이 이미 있습니다": "已存在同名项目",
|
||||||
"같은 이름이 이미 있습니다: {0}": "已存在同名项目: {0}",
|
"같은 이름이 이미 있습니다: {0}": "已存在同名项目: {0}",
|
||||||
|
|
@ -641,6 +643,7 @@
|
||||||
"다른 앱에서 동영상을 열 때": "从其他应用打开视频时",
|
"다른 앱에서 동영상을 열 때": "从其他应用打开视频时",
|
||||||
"다른 앱에서 열기: {0}": "从其他应用打开: {0}",
|
"다른 앱에서 열기: {0}": "从其他应用打开: {0}",
|
||||||
"다른 앱으로 열기": "用其他应用打开",
|
"다른 앱으로 열기": "用其他应用打开",
|
||||||
|
"다른 언어로 다시": "用其他语言重做",
|
||||||
"다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. 주소창 옆 번역 버튼으로 원문 · 번역을 바꿀 수 있습니다.": "将其他语言的页面翻译为界面语言 ({0})。页面文字会发送到 Google 翻译。可用地址栏旁的翻译按钮切换原文 · 译文。",
|
"다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. 주소창 옆 번역 버튼으로 원문 · 번역을 바꿀 수 있습니다.": "将其他语言的页面翻译为界面语言 ({0})。页面文字会发送到 Google 翻译。可用地址栏旁的翻译按钮切换原文 · 译文。",
|
||||||
"다른 언어로 번역 (AI · 이 {0}에서)": "翻译为其他语言(AI · 在此{0}上)",
|
"다른 언어로 번역 (AI · 이 {0}에서)": "翻译为其他语言(AI · 在此{0}上)",
|
||||||
"다른 이름으로 저장": "另存为",
|
"다른 이름으로 저장": "另存为",
|
||||||
|
|
@ -1415,6 +1418,7 @@
|
||||||
"원어({0})와 같은 언어로는 번역하지 않습니다.": "不会翻译成与原语言({0})相同的语言。",
|
"원어({0})와 같은 언어로는 번역하지 않습니다.": "不会翻译成与原语言({0})相同的语言。",
|
||||||
"원어: {0} ({1})": "原语言: {0}({1})",
|
"원어: {0} ({1})": "原语言: {0}({1})",
|
||||||
"원어: 자동 감지 (글자로 추정)": "原语言: 自动检测(根据文字推测)",
|
"원어: 자동 감지 (글자로 추정)": "原语言: 自动检测(根据文字推测)",
|
||||||
|
"원어를 골라 다시 만들기": "选择原语言并重新生成",
|
||||||
"원인: {0}": "原因: {0}",
|
"원인: {0}": "原因: {0}",
|
||||||
"월": "一",
|
"월": "一",
|
||||||
"웹 브라우저": "网页浏览器",
|
"웹 브라우저": "网页浏览器",
|
||||||
|
|
@ -1849,6 +1853,7 @@
|
||||||
"처음 쓸 때 내려받기 (기본)": "首次使用时下载 (默认)",
|
"처음 쓸 때 내려받기 (기본)": "首次使用时下载 (默认)",
|
||||||
"처음 아이콘 (파랑)": "最初的图标(蓝色)",
|
"처음 아이콘 (파랑)": "最初的图标(蓝色)",
|
||||||
"처음대로": "恢复默认",
|
"처음대로": "恢复默认",
|
||||||
|
"처음부터 정하려면 AI 자막 창의 \"원어 (영상 속 언어)\" 에서 고르세요.": "如需一开始就指定,请在 AI 字幕窗口的“原语言(视频中的语言)”中选择。",
|
||||||
"처음으로: {0}": "回到第一个: {0}",
|
"처음으로: {0}": "回到第一个: {0}",
|
||||||
"첫 장입니다.": "已是第一页。",
|
"첫 장입니다.": "已是第一页。",
|
||||||
"첫 줄 추가": "添加第一行",
|
"첫 줄 추가": "添加第一行",
|
||||||
|
|
|
||||||
|
|
@ -943,6 +943,8 @@ class AppController extends ChangeNotifier {
|
||||||
? opts.source
|
? opts.source
|
||||||
: detectLanguage(cues.map((c) => c.text).join(' '));
|
: detectLanguage(cues.map((c) => c.text).join(' '));
|
||||||
_log(trf('음성인식 완료: {0}줄, 원어 {1}({2})', [cues.length, src.name, src.code]));
|
_log(trf('음성인식 완료: {0}줄, 원어 {1}({2})', [cues.length, src.name, src.code]));
|
||||||
|
// 198: 자동 감지가 틀릴 수 있다 (영어를 한국어로 잡아 영어 발음을 한글로 적음) - 끝난 뒤 알리고 다른 언어로 다시 할 수 있게
|
||||||
|
v.aiDetected = opts.source == undetermined ? src.code : null;
|
||||||
|
|
||||||
// 5. 파일명_AI.srt (원본)
|
// 5. 파일명_AI.srt (원본)
|
||||||
await storage.ensureDirectory(outputDirFor(v.path));
|
await storage.ensureDirectory(outputDirFor(v.path));
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,9 @@ class VideoItem {
|
||||||
String? message;
|
String? message;
|
||||||
String? outputPath;
|
String? outputPath;
|
||||||
|
|
||||||
|
/// 198: 마지막 AI 자막에서 원어를 자동 감지했으면 그 언어 코드 (원어를 직접 골랐으면 null)
|
||||||
|
String? aiDetected;
|
||||||
|
|
||||||
VideoItem(this.path);
|
VideoItem(this.path);
|
||||||
|
|
||||||
String get fileName => p.basename(path);
|
String get fileName => p.basename(path);
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,16 @@ import '../l10n/tr.dart';
|
||||||
Future<void> showAiDialog(BuildContext context, AppController c, VideoItem current,
|
Future<void> showAiDialog(BuildContext context, AppController c, VideoItem current,
|
||||||
{List<VideoItem>? targets, bool thenBuild = false}) async {
|
{List<VideoItem>? targets, bool thenBuild = false}) async {
|
||||||
final list = targets ?? [current];
|
final list = targets ?? [current];
|
||||||
Future<void> run(List<VideoItem> v, AiOptions o) =>
|
// 198: 끝난 뒤 알림 · 다시 하기 창을 띄울 곳 (이 화면이 닫혀도 남는 것)
|
||||||
thenBuild ? c.aiThenBuild(v, o) : c.generateAiSubtitles(v, o);
|
final messenger = ScaffoldMessenger.maybeOf(context);
|
||||||
|
final overlay = Navigator.maybeOf(context)?.overlay;
|
||||||
|
Future<void> run(List<VideoItem> v, AiOptions o) async {
|
||||||
|
await (thenBuild ? c.aiThenBuild(v, o) : c.generateAiSubtitles(v, o));
|
||||||
|
if (o.source == undetermined && messenger != null) {
|
||||||
|
showDetectedLanguage(messenger, overlay, v, onRetry: (lang) => run(v, o.copyWith(source: lang)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<(AiOptions, bool)?> full() => showDialog<(AiOptions, bool)>(
|
Future<(AiOptions, bool)?> full() => showDialog<(AiOptions, bool)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => _AiDialog(c: c, videos: list, videoCount: targets == null ? c.videos.length : 1, thenBuild: thenBuild),
|
builder: (_) => _AiDialog(c: c, videos: list, videoCount: targets == null ? c.videos.length : 1, thenBuild: thenBuild),
|
||||||
|
|
@ -43,6 +51,50 @@ Future<void> showAiDialog(BuildContext context, AppController c, VideoItem curre
|
||||||
await run(all ? List.of(c.videos) : list, opts);
|
await run(all ? List.of(c.videos) : list, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 198: 원어를 자동 감지했으면 끝난 뒤 "감지한 언어: …" 와 [다른 언어로 다시] (언어 고르기 → 그 원어로 다시 만듦)
|
||||||
|
void showDetectedLanguage(ScaffoldMessengerState messenger, OverlayState? overlay, List<VideoItem> videos,
|
||||||
|
{required Future<void> Function(Language) onRetry}) {
|
||||||
|
final found = [for (final v in videos) if (v.aiDetected != null) (v, languageOf(v.aiDetected))];
|
||||||
|
if (found.isEmpty) return;
|
||||||
|
final names = found.length == 1
|
||||||
|
? found.single.$2.name
|
||||||
|
: found.map((x) => '${x.$2.name} (${x.$1.fileName})').join(' · ');
|
||||||
|
messenger.showSnackBar(SnackBar(
|
||||||
|
duration: const Duration(seconds: 12),
|
||||||
|
content: Text(trf('감지한 언어: {0} - 틀렸으면 다른 언어로 다시 만드세요', [names])),
|
||||||
|
action: overlay == null
|
||||||
|
? null
|
||||||
|
: SnackBarAction(
|
||||||
|
label: tr('다른 언어로 다시'),
|
||||||
|
onPressed: () async {
|
||||||
|
if (!overlay.mounted) return;
|
||||||
|
final lang = await pickSourceLanguage(overlay.context, found.first.$2);
|
||||||
|
if (lang != null) await onRetry(lang);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 198: 원어를 골라 다시 만들 때의 언어 고르기 (원어를 처음부터 정하는 곳도 알린다)
|
||||||
|
Future<Language?> pickSourceLanguage(BuildContext context, Language detected) => showDialog<Language>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => SimpleDialog(
|
||||||
|
title: Text(tr('원어를 골라 다시 만들기')),
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 0, 24, 8),
|
||||||
|
child: Text(tr('처음부터 정하려면 AI 자막 창의 "원어 (영상 속 언어)" 에서 고르세요.'),
|
||||||
|
style: const TextStyle(fontSize: 12, color: JjColors.textDim)),
|
||||||
|
),
|
||||||
|
for (final l in languages)
|
||||||
|
SimpleDialogOption(
|
||||||
|
onPressed: () => Navigator.pop(ctx, l),
|
||||||
|
child: Text('${l.name} (${l.code})${l == detected ? ' ${tr('- 감지한 언어')}' : ''}'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
/// 138: "매번 묻기" 를 껐을 때 시작 전에 [취소] 할 수 있는 시간
|
/// 138: "매번 묻기" 를 껐을 때 시작 전에 [취소] 할 수 있는 시간
|
||||||
const aiStartDelay = Duration(seconds: 3);
|
const aiStartDelay = Duration(seconds: 3);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,4 +79,28 @@ void main() {
|
||||||
await done;
|
await done;
|
||||||
expect(c.busy, isFalse);
|
expect(c.busy, isFalse);
|
||||||
});
|
});
|
||||||
|
testWidgets('198: 원어를 자동 감지했으면 끝난 뒤 "감지한 언어" 와 [다른 언어로 다시] (고른 원어로 다시)', (tester) async {
|
||||||
|
final key = GlobalKey<NavigatorState>();
|
||||||
|
await tester.pumpWidget(MaterialApp(navigatorKey: key, home: const Scaffold(body: SizedBox())));
|
||||||
|
final v = VideoItem(r'D:\v\a.mp4')..aiDetected = 'ko';
|
||||||
|
Language? again;
|
||||||
|
showDetectedLanguage(tester.state<ScaffoldMessengerState>(find.byType(ScaffoldMessenger)), key.currentState!.overlay, [v],
|
||||||
|
onRetry: (l) async => again = l);
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 800)); // 알림이 다 올라올 때까지
|
||||||
|
expect(find.text('감지한 언어: 한국어 - 틀렸으면 다른 언어로 다시 만드세요'), findsOneWidget);
|
||||||
|
await tester.tap(find.text('다른 언어로 다시'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('원어를 골라 다시 만들기'), findsOneWidget);
|
||||||
|
expect(find.textContaining('- 감지한 언어'), findsOneWidget);
|
||||||
|
await tester.tap(find.textContaining('영어 (en)'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(again?.code, 'en');
|
||||||
|
// 원어를 직접 고른 동영상 (감지 안 함) 만 있으면 알리지 않는다
|
||||||
|
final w = VideoItem(r'D:\v\b.mp4');
|
||||||
|
showDetectedLanguage(tester.state<ScaffoldMessengerState>(find.byType(ScaffoldMessenger)), key.currentState!.overlay, [w],
|
||||||
|
onRetry: (_) async {});
|
||||||
|
await tester.pump(const Duration(seconds: 13));
|
||||||
|
expect(find.textContaining('감지한 언어:'), findsNothing);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue