replace vue-i18n with i18next-vue
Complete the i18n migration by switching runtime setup and remaining components to i18next-vue, and add shared locale constants/helpers for SSR and client language handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { VueHead } from '@/components/VueHead';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
import { client } from '@/api/client';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { reactive } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { z } from 'zod';
|
||||
|
||||
const toast = useAppToast();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = reactive({
|
||||
email: ''
|
||||
|
||||
@@ -80,13 +80,13 @@
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { z } from 'zod';
|
||||
|
||||
const toast = useAppToast();
|
||||
const auth = useAuthStore();
|
||||
const showPassword = ref(false);
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = reactive({
|
||||
email: '',
|
||||
|
||||
@@ -51,12 +51,12 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { z } from 'zod';
|
||||
|
||||
const auth = useAuthStore();
|
||||
const showPassword = ref(false);
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
|
||||
@@ -173,13 +173,13 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const { t, tm } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getFeatureList = (key: string): string[] => {
|
||||
const localized = tm(key);
|
||||
const localized = t(key, { returnObjects: true });
|
||||
return Array.isArray(localized) ? localized.map((item) => String(item)) : [];
|
||||
};
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Head } from '@unhead/vue/components'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const pageContent = computed(() => {
|
||||
const title = t('legal.privacy.title');
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useHead } from '@unhead/vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const pageContent = computed(() => {
|
||||
const title = t('legal.terms.title');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import PageHeader from '@/components/dashboard/PageHeader.vue';
|
||||
import NotificationActions from './components/NotificationActions.vue';
|
||||
import NotificationList from './components/NotificationList.vue';
|
||||
@@ -21,7 +21,7 @@ interface Notification {
|
||||
|
||||
const loading = ref(false);
|
||||
const activeTab = ref('all');
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const notifications = ref<Notification[]>([
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
interface Props {
|
||||
loading?: boolean;
|
||||
@@ -13,7 +13,7 @@ const emit = defineEmits<{
|
||||
clearAll: [];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import InfoIcon from '@/components/icons/InfoIcon.vue';
|
||||
import CheckCircleIcon from '@/components/icons/CheckCircleIcon.vue';
|
||||
import AlertTriangleIcon from '@/components/icons/AlertTriangleIcon.vue';
|
||||
@@ -32,7 +32,7 @@ const emit = defineEmits<{
|
||||
delete: [id: string];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const iconComponent = computed(() => {
|
||||
const icons: Record<string, any> = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import NotificationItem from './NotificationItem.vue';
|
||||
|
||||
interface Notification {
|
||||
@@ -24,7 +24,7 @@ const emit = defineEmits<{
|
||||
delete: [id: string];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { client, type ModelVideo } from '@/api/client';
|
||||
import PageHeader from '@/components/dashboard/PageHeader.vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import NameGradient from './components/NameGradient.vue';
|
||||
import QuickActions from './components/QuickActions.vue';
|
||||
import RecentVideos from './components/RecentVideos.vue';
|
||||
@@ -10,7 +10,7 @@ import StatsOverview from './components/StatsOverview.vue';
|
||||
|
||||
const loading = ref(true);
|
||||
const recentVideos = ref<ModelVideo[]>([]);
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const stats = ref({
|
||||
totalVideos: 0,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const auth = useAuthStore();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import Chart from '@/components/icons/Chart.vue';
|
||||
import Credit from '@/components/icons/Credit.vue';
|
||||
import Upload from '@/components/icons/Upload.vue';
|
||||
@@ -17,7 +17,7 @@ defineProps<Props>();
|
||||
|
||||
const uiState = useUIState();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const quickActions = computed(() => [
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ModelVideo } from '@/api/client';
|
||||
import EmptyState from '@/components/dashboard/EmptyState.vue';
|
||||
import { formatDate, formatDuration } from '@/lib/utils';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
interface Props {
|
||||
@@ -13,7 +13,7 @@ interface Props {
|
||||
defineProps<Props>();
|
||||
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getStatusClass = (status?: string) => {
|
||||
switch (status?.toLowerCase()) {
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const auth = useAuthStore();
|
||||
const isCopied = ref(false);
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const url = computed(() => `${location.origin}/ref/${auth.user?.username || ''}`);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import StatsCard from '@/components/dashboard/StatsCard.vue';
|
||||
import { formatBytes } from '@/lib/utils';
|
||||
|
||||
@@ -15,7 +15,7 @@ interface Props {
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { formatBytes } from '@/lib/utils';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
interface Props {
|
||||
loading: boolean;
|
||||
@@ -13,7 +13,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const storagePercentage = computed(() => {
|
||||
return Math.round((props.stats.storageUsed / props.stats.storageLimit) * 100);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const auth = useAuthStore();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import PageHeader from '@/components/dashboard/PageHeader.vue';
|
||||
import AppConfirmHost from '@/components/app/AppConfirmHost.vue';
|
||||
@@ -80,7 +80,7 @@ import VideoPlayIcon from '@/components/icons/VideoPlayIcon.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const auth = useAuthStore();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
// Map tab values to their paths
|
||||
const tabPaths: Record<string, string> = {
|
||||
profile: '/settings',
|
||||
|
||||
@@ -6,7 +6,7 @@ import CheckIcon from '@/components/icons/CheckIcon.vue';
|
||||
import LockIcon from '@/components/icons/LockIcon.vue';
|
||||
import TelegramIcon from '@/components/icons/TelegramIcon.vue';
|
||||
import XIcon from '@/components/icons/XIcon.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
dialogVisible: boolean;
|
||||
@@ -31,7 +31,7 @@ const emit = defineEmits<{
|
||||
(e: 'disconnect-telegram'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangePassword = () => {
|
||||
emit('change-password');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import AppButton from '@/components/app/AppButton.vue';
|
||||
import AppInput from '@/components/app/AppInput.vue';
|
||||
import AppProgressBar from '@/components/app/AppProgressBar.vue';
|
||||
@@ -12,7 +12,7 @@ import UserIcon from '@/components/icons/UserIcon.vue';
|
||||
import XIcon from '@/components/icons/XIcon.vue';
|
||||
|
||||
const auth = useAuthStore();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const props = defineProps<{
|
||||
editing: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import AppButton from '@/components/app/AppButton.vue';
|
||||
import AppDialog from '@/components/app/AppDialog.vue';
|
||||
import AppInput from '@/components/app/AppInput.vue';
|
||||
@@ -33,7 +33,7 @@ const emit = defineEmits<{
|
||||
const twoFactorDialogVisible = ref(false);
|
||||
const twoFactorCode = ref('');
|
||||
const twoFactorSecret = ref('JBSWY3DPEHPK3PXP');
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleToggle2FA = async () => {
|
||||
if (!props.twoFactorEnabled) {
|
||||
|
||||
@@ -12,11 +12,11 @@ import TrashIcon from '@/components/icons/TrashIcon.vue';
|
||||
import { useAppConfirm } from '@/composables/useAppConfirm';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const toast = useAppToast();
|
||||
const confirm = useAppConfirm();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
interface VastTemplate {
|
||||
id: string;
|
||||
|
||||
@@ -13,11 +13,12 @@ import { useAppToast } from '@/composables/useAppToast';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { useQuery } from '@pinia/colada';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { getActiveI18n } from '@/i18n';
|
||||
|
||||
const toast = useAppToast();
|
||||
const auth = useAuthStore();
|
||||
const { t, locale } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
key: () => ['payments-and-plans'],
|
||||
@@ -93,7 +94,7 @@ const getStatusLabel = (status: string) => {
|
||||
return map[status] || status;
|
||||
};
|
||||
|
||||
const currencyFormatter = computed(() => new Intl.NumberFormat(locale.value === 'vi' ? 'vi-VN' : 'en-US', {
|
||||
const currencyFormatter = computed(() => new Intl.NumberFormat(getActiveI18n()?.resolvedLanguage === 'vi' ? 'vi-VN' : 'en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
maximumFractionDigits: 2,
|
||||
@@ -118,7 +119,7 @@ const subscribe = async (plan: ModelPlan) => {
|
||||
|
||||
paymentHistory.value.unshift({
|
||||
id: `inv_${Date.now()}`,
|
||||
date: new Date().toLocaleDateString(locale.value === 'vi' ? 'vi-VN' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric' }),
|
||||
date: new Date().toLocaleDateString(getActiveI18n()?.resolvedLanguage === 'vi' ? 'vi-VN' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric' }),
|
||||
amount: plan.price || 0,
|
||||
plan: plan.name || t('settings.billing.unknownPlan'),
|
||||
status: 'success',
|
||||
|
||||
@@ -6,11 +6,11 @@ import SlidersIcon from '@/components/icons/SlidersIcon.vue';
|
||||
import TrashIcon from '@/components/icons/TrashIcon.vue';
|
||||
import { useAppConfirm } from '@/composables/useAppConfirm';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const toast = useAppToast();
|
||||
const confirm = useAppConfirm();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleDeleteAccount = () => {
|
||||
confirm.require({
|
||||
|
||||
@@ -11,11 +11,11 @@ import TrashIcon from '@/components/icons/TrashIcon.vue';
|
||||
import { useAppConfirm } from '@/composables/useAppConfirm';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const toast = useAppToast();
|
||||
const confirm = useAppConfirm();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const domains = ref([
|
||||
{ id: '1', name: 'example.com', addedAt: '2024-01-15' },
|
||||
|
||||
@@ -8,10 +8,10 @@ import SendIcon from '@/components/icons/SendIcon.vue';
|
||||
import TelegramIcon from '@/components/icons/TelegramIcon.vue';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const toast = useAppToast();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const notificationSettings = ref({
|
||||
email: true,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import AppButton from '@/components/app/AppButton.vue';
|
||||
import AppSwitch from '@/components/app/AppSwitch.vue';
|
||||
import CheckIcon from '@/components/icons/CheckIcon.vue';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
|
||||
const toast = useAppToast();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const playerSettings = ref({
|
||||
autoplay: true,
|
||||
|
||||
@@ -13,12 +13,12 @@ import { normalizeLocale } from '@/i18n';
|
||||
import { useAppConfirm } from '@/composables/useAppConfirm';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const auth = useAuthStore();
|
||||
const toast = useAppToast();
|
||||
const confirm = useAppConfirm();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const selectedLanguage = ref<SupportedLocale>(normalizeLocale((auth.user as any)?.language ?? (auth.user as any)?.locale));
|
||||
const languageSaving = ref(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useUploadQueue } from '@/composables/useUploadQueue';
|
||||
import { useUIState } from '@/stores/uiState';
|
||||
import RemoteUrlForm from './components/RemoteUrlForm.vue';
|
||||
@@ -8,7 +8,7 @@ import UploadDropzone from './components/UploadDropzone.vue';
|
||||
|
||||
const uiState = useUIState();
|
||||
const mode = ref<'local' | 'remote'>('local');
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { addFiles, addRemoteUrls, pendingCount, startQueue, remainingSlots, maxItems } = useUploadQueue();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
defineProps<{
|
||||
pendingCount?: number;
|
||||
@@ -9,7 +9,7 @@ defineProps<{
|
||||
|
||||
const category = ref('');
|
||||
const visibility = ref('public');
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{ maxUrls?: number }>();
|
||||
const urls = ref('');
|
||||
|
||||
const emit = defineEmits<{ submit: [urls: string[]] }>();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSubmit = () => {
|
||||
const limit = props.maxUrls ?? 5;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{ maxFiles?: number }>();
|
||||
const emit = defineEmits<{ filesSelected: [files: FileList] }>();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isDragOver = ref(false);
|
||||
let dragCounter = 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: 'local' | 'remote';
|
||||
@@ -11,7 +11,7 @@ const emit = defineEmits<{
|
||||
'update:modelValue': [value: 'local' | 'remote'];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const modeList = computed<{ id: 'local' | 'remote'; label: string; icon: string }[]>(() => [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import UploadQueueItem from './UploadQueueItem.vue';
|
||||
import type { QueueItem } from '@/composables/useUploadQueue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
defineProps<{
|
||||
items?: QueueItem[];
|
||||
@@ -17,7 +17,7 @@ const emit = defineEmits<{
|
||||
startQueue: [];
|
||||
}>()
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import FileUploadType from '@/components/icons/FileUploadType.vue';
|
||||
import type { QueueItem } from '@/composables/useUploadQueue';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
item: QueueItem;
|
||||
@@ -13,7 +13,7 @@ const emit = defineEmits<{
|
||||
cancel: [id: string];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
switch (props.item.status) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ModelVideo } from '@/api/client';
|
||||
import { fetchMockVideoById } from '@/mocks/videos';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
videoId: string;
|
||||
@@ -17,7 +17,7 @@ const toast = useAppToast();
|
||||
const video = ref<ModelVideo | null>(null);
|
||||
const loading = ref(true);
|
||||
const copiedField = ref<string | null>(null);
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const fetchVideo = async () => {
|
||||
loading.value = true;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { deleteMockVideo, fetchMockVideoById, updateMockVideo } from '@/mocks/vi
|
||||
import { useAppConfirm } from '@/composables/useAppConfirm';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import VideoEditForm from './components/Detail/VideoEditForm.vue';
|
||||
import VideoHeader from './components/Detail/VideoInfoHeader.vue';
|
||||
@@ -16,7 +16,7 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const toast = useAppToast();
|
||||
const confirm = useAppConfirm();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const videoId = route.params.id as string;
|
||||
const video = ref<ModelVideo | null>(null);
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { ModelVideo } from '@/api/client';
|
||||
import { fetchMockVideoById, updateMockVideo } from '@/mocks/videos';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
videoId: string;
|
||||
@@ -17,7 +17,7 @@ const toast = useAppToast();
|
||||
const video = ref<ModelVideo | null>(null);
|
||||
const loading = ref(true);
|
||||
const saving = ref(false);
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = ref({
|
||||
title: '',
|
||||
|
||||
@@ -4,7 +4,7 @@ import EmptyState from '@/components/dashboard/EmptyState.vue';
|
||||
import PageHeader from '@/components/dashboard/PageHeader.vue';
|
||||
import { fetchMockVideos } from '@/mocks/videos';
|
||||
import { createStaticVNode, computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useUploadQueue } from '@/composables/useUploadQueue';
|
||||
@@ -23,7 +23,7 @@ const uiState = useUIState();
|
||||
const { addFiles, startQueue } = useUploadQueue();
|
||||
const toast = useAppToast();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
const videos = ref<ModelVideo[]>([]);
|
||||
const loading = ref(true);
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
@@ -45,7 +45,7 @@ import EllipsisVerticalIcon from '@/components/icons/EllipsisVerticalIcon.vue';
|
||||
import type { ModelVideo } from '@/api/client';
|
||||
import { useAppToast } from '@/composables/useAppToast';
|
||||
import { computed, nextTick, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -61,7 +61,7 @@ const isOpen = ref(false);
|
||||
const containerRef = ref<HTMLElement>();
|
||||
const menuRef = ref<HTMLElement>();
|
||||
const menuStyle = ref<Record<string, string>>({});
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const videoUrl = computed(() => {
|
||||
return `${window.location.origin}/videos/${props.video.id}`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
defineProps<{
|
||||
title: string;
|
||||
@@ -14,7 +14,7 @@ const emit = defineEmits<{
|
||||
toggleEdit: [];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import type { ModelVideo } from '@/api/client';
|
||||
import { formatBytes, getStatusSeverity } from '@/lib/utils';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import { getActiveI18n } from '@/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
video: ModelVideo;
|
||||
@@ -14,7 +15,7 @@ const emit = defineEmits<{
|
||||
delete: [];
|
||||
}>();
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formatFileSize = (bytes?: number): string => {
|
||||
if (!bytes) return '-';
|
||||
@@ -35,7 +36,7 @@ const formatDuration = (seconds?: number): string => {
|
||||
const formatDate = (dateStr?: string): string => {
|
||||
if (!dateStr) return '-';
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString(locale.value === 'vi' ? 'vi-VN' : 'en-US', {
|
||||
return date.toLocaleString(getActiveI18n()?.resolvedLanguage === 'vi' ? 'vi-VN' : 'en-US', {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelVideo } from '@/api/client';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
video: ModelVideo;
|
||||
@@ -11,7 +11,7 @@ const emit = defineEmits<{
|
||||
copy: [text: string, label: string];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleCopy = (text: string, label: string) => {
|
||||
emit('copy', text, label);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelVideo } from '@/api/client';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
defineProps<{
|
||||
video: ModelVideo;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelVideo } from '@/api/client';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
defineProps<{
|
||||
selectedVideos: ModelVideo[];
|
||||
@@ -11,7 +11,7 @@ const emit = defineEmits<{
|
||||
(e: 'clear'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
searchQuery: string;
|
||||
@@ -19,7 +19,7 @@ const emit = defineEmits<{
|
||||
(e: 'search'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
const pageCount = computed(() => Math.ceil(props.total / props.limit) || 1);
|
||||
const first = computed(() => Math.min((props.page - 1) * props.limit + 1, props.total));
|
||||
const last = computed(() => Math.min(props.page * props.limit, props.total));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelVideo } from '@/api/client';
|
||||
import { formatDate, formatDuration, getStatusSeverity } from '@/lib/utils';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
import CardPopover from './CardPopover.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -15,7 +15,7 @@ const emit = defineEmits<{
|
||||
(e: 'delete', videoId: string): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const severityClasses: Record<string, string> = {
|
||||
success: 'bg-green-100 text-green-800',
|
||||
|
||||
@@ -5,7 +5,7 @@ import PencilIcon from '@/components/icons/PencilIcon.vue';
|
||||
import TrashIcon from '@/components/icons/TrashIcon.vue';
|
||||
import VideoIcon from '@/components/icons/VideoIcon.vue';
|
||||
import { formatBytes, formatDate, getStatusSeverity } from '@/lib/utils';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTranslation } from 'i18next-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
videos: ModelVideo[];
|
||||
@@ -20,7 +20,7 @@ const emit = defineEmits<{
|
||||
(e: 'copy', videoId: string): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const severityClasses: Record<string, string> = {
|
||||
success: 'bg-green-100 text-green-800',
|
||||
|
||||
Reference in New Issue
Block a user