// Vietnamese dictionary — the source of truth for key names (`en.ts` is typed // as `Dict`, so a missing or misspelled key there is a typecheck error). // Not translated anywhere, in any language: film-sim names, user recipe names, // user watermark text — they are proper nouns, see WEB_PLAN.md. export const vi = { 'lang.name': 'Tiếng Việt', 'nav.demo': 'DÙNG THỬ', 'nav.login': 'ĐĂNG NHẬP', 'nav.signup': 'ĐĂNG KÝ', 'nav.logout': 'Đăng xuất', 'nav.account': 'Tài khoản', 'nav.theme': 'Giao diện', 'nav.language': 'Ngôn ngữ', 'nav.back': 'Về trang chủ', 'nav.admin': 'Quản trị', 'nav.photos': 'Ảnh của tôi', 'nav.profile': 'Hồ sơ', 'nav.studio': 'Studio', 'nav.guest': 'Khách', 'act.undo': 'HOÀN TÁC', 'act.reset': 'ĐẶT LẠI', 'act.export': 'XUẤT ẢNH', 'act.exporting': 'Đang xuất…', 'act.cancel': 'Huỷ', 'drop.title': 'Kéo & thả ảnh vào đây', 'drop.hint': 'hoặc bấm để chọn ảnh · dán từ clipboard bằng Ctrl+V', 'drop.replace': 'Đổi ảnh khác', 'drop.peeking': 'ĐANG XEM ẢNH GỐC', 'drop.peekHint': 'Giữ chuột phải hoặc phím \\ để so ảnh gốc', 'drop.compare': 'So với ảnh gốc', 'drop.demoLimit': 'Bản demo: ảnh xuất ra tối đa {px}px và có watermark.', // The histogram overlay. 'HISTOGRAM' is the word Vietnamese photographers use. 'hist.title': 'HISTOGRAM', 'hist.drag': 'Kéo để di chuyển', 'hist.hide': 'Ẩn histogram', 'tab.presets': 'PRESETS', 'tab.favorited': 'FAVORITED', 'tab.photos': 'ẢNH CỦA TÔI', 'tab.light': 'LIGHT', 'tab.wb': 'WB', 'tab.fx': 'FX', 'tab.frame': 'FRAME', 'tab.create': 'CREATE RECIPES', 'create.title': 'TẠO CÔNG THỨC', 'create.save': 'LƯU CÔNG THỨC', 'create.export': 'XUẤT TỆP .RECIPE', 'create.guestHint': 'Khách: công thức chỉ nằm trong RECIPES tới khi đóng trang. Đăng nhập để lưu vào tài khoản và xuất tệp.', 'create.userHint': 'Đã lưu vào tài khoản. Xuất tệp .recipe để chia sẻ — tệp được mã hóa đúng như trên app.', 'sec.filmSims': 'PHOTO STYLE', 'sec.recipes': 'RECIPES', 'sec.saved': 'ĐÃ LƯU', 'sec.savedEmpty': 'Chưa lưu công thức nào. Đăng nhập rồi bấm LƯU để đồng bộ.', 'sec.frame': 'KHUNG', 'sec.crop': 'CẮT', 'sec.rotate': 'XOAY', 'sec.watermark': 'WATERMARK', 'sec.gps': 'GPS', 'wb.temp': 'COLOR TEMP', 'frame.none': 'KHÔNG KHUNG', 'frame.polaroid': 'RETRO INSTANT', 'frame.classic': 'VIỀN CLASSIC', 'frame.cinematic': 'ĐIỆN ẢNH', 'frame.wall': 'WALL FRAME PORTRAIT', 'frame.wallLand': 'WALL FRAME LANDSCAPE', 'crop.none': 'KHÔNG CẮT', 'crop.1:1': '1:1', 'crop.2:3': '2:3', 'crop.3:2': '3:2', 'crop.3:4': '3:4', 'crop.4:3': '4:3', 'crop.16:9': '16:9', 'crop.free': 'TỰ DO', 'crop.apply': 'ÁP DỤNG', 'crop.cancel': 'HUỶ', 'rot.0': '0°', 'rot.90': '90°', 'rot.180': '180°', 'rot.270': '270°', 'wm.text': 'CHỮ', 'wm.color': 'MÀU', 'wm.size': 'CỠ', 'wm.gps': 'DẤU GPS', 'wm.place': 'TÊN ĐỊA ĐIỂM', 'wm.placePh': 'VD: HÀ NỘI', 'wm.manual': 'NHẬP TOẠ ĐỘ', 'wm.lat': 'Vĩ độ', 'wm.lng': 'Kinh độ', 'wm.hasExif': 'Đã đọc GPS từ ảnh.', 'wm.noExif': 'Ảnh không có GPS — nhập tay nếu muốn đóng dấu.', 'auth.loginTitle': 'Đăng nhập', 'auth.signupTitle': 'Tạo tài khoản', 'auth.email': 'Email', 'auth.password': 'Mật khẩu', 'auth.passwordHint': 'Ít nhất 8 ký tự', 'auth.noAccount': 'Chưa có tài khoản? Đăng ký', 'auth.haveAccount': 'Đã có tài khoản? Đăng nhập', 'auth.continueDemo': 'Tiếp tục dùng thử', 'auth.busy': 'Đang xử lý…', 'auth.loggedInAs': 'Đã đăng nhập: {email}', 'save.title': 'Lưu công thức', 'save.name': 'Tên công thức', 'save.recipes': 'LƯU CÔNG THỨC', 'save.saved': 'Đã lưu "{name}"', 'save.needLogin': 'Đăng nhập để lưu công thức.', 'save.photo': 'LƯU ẢNH {n}/{max}', 'save.photoNeedLogin': 'Đăng nhập để lưu ảnh.', 'save.photoSaved': 'Đã lưu ảnh {n}/{max}.', 'save.photoFull': 'Đã đủ {max} ảnh — xoá bớt trong Ảnh của tôi.', 'save.photoNoSource': 'Chưa có ảnh nào để lưu.', 'save.photoFailed': 'Không lưu được ảnh: {msg}', 'myp.newOn': 'BẢN LƯU MỚI: LÊN DẢI PHIM', 'myp.newOff': 'BẢN LƯU MỚI: RIÊNG TƯ', 'myp.thisOn': 'ẢNH NÀY: LÊN DẢI PHIM', 'myp.thisOff': 'ẢNH NÀY: RIÊNG TƯ', 'myp.empty': 'CHƯA LƯU ẢNH NÀO.', 'myp.history': 'LỊCH SỬ', 'myp.historyEmpty': 'CHƯA CÓ MỐC LƯU NÀO TRƯỚC ĐÓ.', 'myp.consent': 'Cho phép ảnh này lên landing page', 'photo.contribute': 'ĐÓNG GÓP ẢNH', 'photo.uploading': 'ĐANG TẢI…', 'photo.thanks': 'Ảnh của bạn đã vào dải phim.', 'photo.failed': 'Không tải được ảnh: {msg}', 'photos.title': 'Ảnh của tôi', 'photos.hint': 'Mỗi tài khoản giữ tối đa {max} ảnh. Ảnh lưu từ studio nằm ở đây và xuất hiện trên dải phim cộng đồng.', 'photos.count': '{n}/{max} ảnh', 'photos.add': 'THÊM ẢNH', 'photos.empty': 'Chưa có ảnh nào. Mở studio rồi bấm LƯU ẢNH, hoặc thêm ảnh ngay ở đây.', 'photos.full': 'Đã đủ {max} ảnh. Xoá bớt trước khi lưu tiếp.', 'photos.delete': 'XOÁ', 'photos.deleted': 'Đã xoá ảnh.', 'photos.failed': 'Không thực hiện được: {msg}', 'photos.noLabels': 'Chưa có nhãn dải phim', 'adm.title': 'Quản trị dải phim', 'adm.subtitle': 'Ảnh do người dùng đóng góp. Xoá một ảnh để gỡ nó khỏi trang chủ.', 'adm.upload': 'THÊM ẢNH', 'adm.slot': 'Hiển thị ở', 'adm.slotStrip': 'Dải phim cộng đồng', 'adm.slotTester': 'Live tester (lp-tester)', 'adm.slotCreator': 'Mục công thức live (lp-sec)', 'adm.slotQr': 'Thẻ QR (lp-qr)', 'adm.slotHint': 'Ba vị trí live mỗi lần vào trang sẽ bốc ngẫu nhiên một ảnh trong nhóm, nên nhiều ảnh cùng một vị trí sẽ luân phiên nhau.', 'adm.signIn': 'Đăng nhập bằng tài khoản quản trị', 'adm.notAdmin': 'Tài khoản này không có quyền quản trị.', 'adm.empty': 'Chưa có ảnh đóng góp nào.', 'adm.total': '{n} ảnh', 'adm.owner': 'Người đăng', 'adm.size': 'Dung lượng', 'adm.uploaded': 'Thời điểm', 'adm.delete': 'XOÁ', 'adm.clearAll': 'XOÁ TẤT CẢ', 'adm.clearConfirm': 'Xoá toàn bộ {n} ảnh đóng góp? Không hoàn tác được.', 'adm.removed': 'Đã xoá {n} ảnh.', 'adm.back': 'Về trang chủ', 'adm.settings': 'CÀI ĐẶT', 'adm.tabPictures': 'Ảnh', 'adm.tabUsers': 'Người dùng', 'adm.tabProfile': 'Hồ sơ', 'adm.close': 'Đóng', 'adm.userPicture': 'Ảnh', 'adm.userName': 'Tên', 'adm.userEmail': 'Email', 'adm.userRole': 'Quyền', 'adm.userPhotos': 'Số ảnh', 'adm.userJoined': 'Ngày tạo', 'adm.roleAdmin': 'Quản trị', 'adm.roleMember': 'Thành viên', 'adm.viewPhotos': 'XEM ẢNH', 'adm.noUsers': 'Chưa có tài khoản nào.', 'adm.showAll': 'TẤT CẢ', 'adm.profileHint': 'Ảnh đại diện, email và mật khẩu của tài khoản đang đăng nhập. Đổi email hoặc mật khẩu cần mật khẩu hiện tại.', 'adm.changeAvatar': 'ĐỔI ẢNH ĐẠI DIỆN', 'adm.currentPassword': 'Mật khẩu hiện tại', 'adm.newEmail': 'Email mới', 'adm.newPassword': 'Mật khẩu mới', 'adm.save': 'LƯU', 'adm.saved': 'Đã lưu hồ sơ.', 'adm.userActions': 'Thao tác', 'adm.userBlock': 'KHOÁ', 'adm.userUnblock': 'MỞ KHOÁ', 'adm.userRemove': 'GỠ', 'adm.userRestore': 'KHÔI PHỤC', 'adm.userDelete': 'XOÁ HẲN', 'adm.userBlocked': 'Đã khoá', 'adm.userRemoved': 'Đã gỡ', 'adm.userDeleteConfirm': 'Xoá vĩnh viễn tài khoản {email} cùng toàn bộ ảnh và công thức của nó? Không hoàn tác được.', 'adm.selectAll': 'CHỌN TẤT CẢ', 'adm.selectNone': 'BỎ CHỌN', 'adm.deleteSelected': 'XOÁ ĐÃ CHỌN ({n})', 'adm.userDeleteSelectedConfirm': 'Xoá vĩnh viễn {n} tài khoản đã chọn cùng toàn bộ ảnh và công thức của chúng? Không hoàn tác được.', 'adm.tabStats': 'THỐNG KÊ', // The traffic screen. Bucket names ('/app', a control's data-key, a browser // version) are machine values and stay as they are. 'stats.days': '{n} NGÀY', 'stats.hint': 'Lưu lượng trong khoảng đã chọn.', 'stats.views': 'LƯỢT XEM', 'stats.clicks': 'LƯỢT NHẤN', 'stats.visitors': 'KHÁCH TRUY CẬP', 'stats.timeline': 'LƯU LƯỢNG THEO NGÀY', 'stats.peak': 'đỉnh', 'stats.pages': 'TRANG ĐƯỢC XEM', 'stats.targets': 'TÍNH NĂNG ĐƯỢC NHẤN', 'stats.countries': 'QUỐC GIA', 'stats.regions': 'VÙNG MIỀN', 'stats.cities': 'THÀNH PHỐ', 'stats.browsers': 'TRÌNH DUYỆT', 'stats.systems': 'HỆ ĐIỀU HÀNH', 'stats.devices': 'THIẾT BỊ', 'stats.empty': 'Chưa có dữ liệu.', 'profile.title': 'Hồ sơ của tôi', 'err.noWebgl': 'Trình duyệt không cấp được WebGL — bản dựng này cần WebGL để xử lý ảnh (chạy CPU chậm gấp hàng nghìn lần).', 'err.decode': 'Không đọc được ảnh này. Thử JPG hoặc PNG khác.', 'err.generic': 'Có lỗi xảy ra: {msg}', 'err.offline': 'Không gọi được máy chủ.', 'meta.exported': 'Đã tải {name} ({kb}KB, 300 ppi)', } as const; // `vi` is `as const` so the key set is exact; `Dict` widens the values to // string, which is what lets `en.ts` hold different text under the same keys // while a missing or misspelled key stays a typecheck error. export type Dict = { [K in keyof typeof vi]: string }; export type MsgKey = keyof Dict;