diff --git a/im-uniapp/common/date.js b/im-uniapp/common/date.js
index 3407a13..2300ca1 100644
--- a/im-uniapp/common/date.js
+++ b/im-uniapp/common/date.js
@@ -1,19 +1,35 @@
+// 先获取全局 i18n(uniapp 通用写法)
+const getI18n = () => {
+ if (typeof getApp !== 'undefined') {
+ return getApp()?.$i18n || null
+ }
+ return null
+}
+
+// 多语言文本获取
+const t = (key) => {
+ const i18n = getI18n()
+ if (!i18n) return key
+ return i18n.t(key)
+}
+
let toTimeText = (timeStamp, simple) => {
var dateTime = new Date(timeStamp)
var currentTime = Date.parse(new Date()); //当前时间
var timeDiff = currentTime - dateTime; //与当前时间误差
var timeText = '';
if (timeDiff <= 60000) { //一分钟内
- timeText = 'Just now';
+ timeText = t('common.justNow');
} else if (timeDiff > 60000 && timeDiff < 3600000) {
//1小时内
- timeText = Math.floor(timeDiff / 60000) + ' minutes ago';
+ const min = Math.floor(timeDiff / 60000)
+ timeText = `${min} ${t('common.minutesAgo')}`;
} else if (timeDiff >= 3600000 && timeDiff < 86400000 && !isYestday(dateTime)) {
//今日
timeText = formatDateTime(dateTime).substr(11, 5);
} else if (isYestday(dateTime)) {
//昨天
- timeText = 'Yesterday ' + formatDateTime(dateTime).substr(11, 5);
+ timeText = t('common.yesterday') + ' ' + formatDateTime(dateTime).substr(11, 5);
} else if (isYear(dateTime)) {
//今年
timeText = formatDateTime(dateTime).substr(5, simple ? 5 : 14);
@@ -54,6 +70,7 @@ let formatDateTime = (date) => {
minute = minute < 10 ? ('0' + minute) : minute
var second = dateObject.getSeconds()
second = second < 10 ? ('0' + second) : second
+ // 修复:这里少了一个 + 号
return y + '/' + m + '/' + d + ' ' + h + ':' + minute + ':' + second
}
diff --git a/im-uniapp/components/chat-message-item/chat-message-item.vue b/im-uniapp/components/chat-message-item/chat-message-item.vue
index 459ef52..dc9a03b 100644
--- a/im-uniapp/components/chat-message-item/chat-message-item.vue
+++ b/im-uniapp/components/chat-message-item/chat-message-item.vue
@@ -17,10 +17,8 @@
-
-
@@ -72,12 +70,12 @@
class="send-fail iconfont icon-warning-circle-fill">
- Read
- Unread
+ {{ $t('chat.read') }}
+ {{ $t('chat.unread') }}
- {{ msgInfo.readedCount }}人已读
+ {{ $t('chat.readedCount', { count: msgInfo.readedCount }) }}
@@ -121,20 +119,17 @@ export default {
this.$emit("resend", this.msgInfo);
},
onPlayAudio() {
- // 初始化音频播放器
if (!this.innerAudioContext) {
this.innerAudioContext = uni.createInnerAudioContext();
let url = this.contentData.url;
this.innerAudioContext.src = url;
this.innerAudioContext.onEnded((e) => {
- console.log('停止')
this.audioPlayState = "STOP"
this.emit();
})
this.innerAudioContext.onError((e) => {
this.audioPlayState = "STOP"
console.log("播放音频出错");
- console.log(e)
this.emit();
});
}
@@ -199,27 +194,27 @@ export default {
if (this.msgInfo.type == this.$enums.MESSAGE_TYPE.TEXT) {
items.push({
key: 'COPY',
- name: '复制',
+ name: this.$t('chat.copy'),
icon: 'bars'
});
}
if (this.msgInfo.selfSend && this.msgInfo.id > 0) {
items.push({
key: 'RECALL',
- name: '撤回',
+ name: this.$t('chat.recall'),
icon: 'refreshempty'
});
}
items.push({
key: 'DELETE',
- name: '删除',
+ name: this.$t('chat.delete'),
icon: 'trash',
color: '#e64e4e'
});
if (this.msgInfo.type == this.$enums.MESSAGE_TYPE.FILE) {
items.push({
key: 'DOWNLOAD',
- name: '下载并打开',
+ name: this.$t('chat.download'),
icon: 'download'
});
}
@@ -245,7 +240,6 @@ export default {
return this.$emo.transform(text, 'emoji-normal')
},
imageStyle() {
- console.log(uni.getSystemInfo())
let maxSize = uni.getSystemInfoSync().windowWidth * 0.6;
let minSize = uni.getSystemInfoSync().windowWidth * 0.2;
let width = this.contentData.width;
@@ -256,7 +250,6 @@ export default {
let h = Math.max(width > height ? ratio * maxSize : maxSize, minSize);
return `width: ${w}px;height:${h}px;`
} else {
- // 兼容历史版本,历史数据没有记录宽高
return `max-width: ${maxSize}px;min-width:100px;max-height: ${maxSize}px;min-height:100px;`
}
}
diff --git a/im-uniapp/main.js b/im-uniapp/main.js
index 8fa350a..a36ae3b 100644
--- a/im-uniapp/main.js
+++ b/im-uniapp/main.js
@@ -10,6 +10,27 @@ import * as messageType from './common/messageType';
import { createSSRApp } from 'vue'
import uviewPlus from '@/uni_modules/uview-plus'
import * as pinia from 'pinia';
+
+// i18n
+import { createI18n } from 'vue-i18n'
+import zh from './static/i18n/zh.json';
+import en from './static/i18n/en.json'
+import jp from './static/i18n/jp.json';
+import kor from './static/i18n/kor.json'
+import vie from './static/i18n/vie.json';
+import ru from './static/i18n/ru.json'
+import de from './static/i18n/de.json'
+import fra from './static/i18n/fra.json'
+import pt from './static/i18n/pt.json'
+import ara from './static/i18n/ara.json'
+
+const savedLang = uni.getStorageSync('app_language') || 'zh'
+const i18n = createI18n({
+ legacy: false,
+ locale: savedLang,
+ messages: { zh, en, jp, kor, vie, ru, de, fra, pt, ara }
+})
+
import useChatStore from '@/store/chatStore.js'
import useFriendStore from '@/store/friendStore.js'
import useGroupStore from '@/store/groupStore.js'
@@ -19,28 +40,30 @@ import barGroup from '@/components/bar/bar-group'
import arrowBar from '@/components/bar/arrow-bar'
import btnBar from '@/components/bar/btn-bar'
import switchBar from '@/components/bar/switch-bar'
+
// #ifdef H5
import * as recorder from './common/recorder-h5';
import ImageResize from "quill-image-resize-mp";
import Quill from "quill";
-// 以下组件用于兼容部分手机聊天边框无法输入的问题
window.Quill = Quill;
window.ImageResize = { default: ImageResize };
-// 调试器
-// import VConsole from 'vconsole'
-// new VConsole();
// #endif
// #ifndef H5
import * as recorder from './common/recorder-app';
// #endif
+
export function createApp() {
const app = createSSRApp(App)
+
app.use(uviewPlus);
app.use(pinia.createPinia());
+ app.use(i18n);
+
app.component('bar-group', barGroup);
app.component('arrow-bar', arrowBar);
app.component('btn-bar', btnBar);
app.component('switch-bar', switchBar);
+
app.config.globalProperties.$http = request;
app.config.globalProperties.$wsApi = socketApi;
app.config.globalProperties.$msgType = messageType;
@@ -50,7 +73,11 @@ export function createApp() {
app.config.globalProperties.$enums = enums;
app.config.globalProperties.$date = date;
app.config.globalProperties.$rc = recorder;
- // 初始化时再挂载store对象
+ app.config.globalProperties.$t = i18n.global.t;
+
+ // 把 i18n 挂到全局,让页面能访问到
+ app.config.globalProperties.$i18n = i18n.global;
+
app.config.globalProperties.$mountStore = () => {
app.config.globalProperties.chatStore = useChatStore();
app.config.globalProperties.friendStore = useFriendStore();
@@ -58,8 +85,6 @@ export function createApp() {
app.config.globalProperties.configStore = useConfigStore();
app.config.globalProperties.userStore = useUserStore();
}
- return {
- app,
- pinia
- }
+
+ return { app, pinia }
}
\ No newline at end of file
diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue
index 429a6a6..2de99fe 100644
--- a/im-uniapp/pages/chat/chat-box.vue
+++ b/im-uniapp/pages/chat/chat-box.vue
@@ -1,10 +1,32 @@
- {{ title }}
+
+ {{ title }}
+
+
+
+
+ {{ langList.find(item => item.value === currentLang)?.label }} ▾
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
-
+
- {{ newMessageSize > 0 ? newMessageSize + "条新消息" : "⬇️" }}
+ {{ newMessageSize > 0 ? $t('chat.newMessages', { count: newMessageSize }) : "⬇️" }}
-
-
-
- 发送
+ {{ $t('chat.send') }}
@@ -139,7 +158,7 @@
>
- 文件
+ {{ $t('chat.file') }}
- 相册
+ {{ $t('chat.album') }}
- 拍摄
+ {{ $t('chat.camera') }}
-
-
-
-
-
{
+ window.location.reload(); // H5 真正强制刷新
+ }, 100);
+ },
loadCommonQuestions(userId) {
this.$http({
url: "/auto/reply/list",
@@ -294,13 +316,11 @@ export default {
);
if (!autoReply) return;
- // 检查是否被封禁
if (this.isBanned) {
this.showBannedTip();
return;
}
- // ========== 第一步:用户发送问题(replyTitle) ==========
let userMsgInfo = {
tmpId: this.generateId(),
type: this.$enums.MESSAGE_TYPE.TEXT,
@@ -316,7 +336,6 @@ export default {
this.chatStore.insertMessage(tmpUserMessage, chat);
this.moveChatToTop();
- // 发送用户消息
this.sendMessageRequest(userMsgInfo)
.then((m) => {
tmpUserMessage = JSON.parse(JSON.stringify(tmpUserMessage));
@@ -324,7 +343,6 @@ export default {
tmpUserMessage.status = m.status;
this.chatStore.updateMessage(tmpUserMessage, chat);
- // ========== 第二步:调用后端接口发送客服回复 ==========
this.triggerAutoReply(autoReply);
this.scrollToBottom();
@@ -337,33 +355,26 @@ export default {
});
},
- // 触发客服自动回复
triggerAutoReply(autoReply) {
- // 生成临时ID用于追踪
let tmpId = this.generateId();
- // 构建客服回复消息
let replyMsgInfo = {
- tmpId: tmpId, // 添加 tmpId
- sendId: this.chat.targetId, // 客服ID作为发送者
- recvId: this.mine.id, // 当前用户作为接收者
- type: autoReply.replyType, // 回复类型
+ tmpId: tmpId,
+ sendId: this.chat.targetId,
+ recvId: this.mine.id,
+ type: autoReply.replyType,
receipt: false
};
- // 根据类型设置内容
if (autoReply.replyType === 0) {
- // 文本回复
replyMsgInfo.content = autoReply.replyContent;
} else if (autoReply.replyType === 1) {
- // 图片回复
replyMsgInfo.content = JSON.stringify({
originUrl: autoReply.replyContent,
thumbUrl: autoReply.replyContent,
});
}
- // 可选:先在前端显示一条临时的"客服正在输入"消息
let typingMessage = {
id: this.generateId(),
tmpId: tmpId,
@@ -372,25 +383,19 @@ export default {
selfSend: false,
sendTime: new Date().getTime(),
type: this.$enums.MESSAGE_TYPE.TIP_TEXT,
- content: "客服正在输入...",
+ content: this.$t('chat.typing'),
status: this.$enums.MESSAGE_STATUS.SENDING
};
this.chatStore.insertMessage(typingMessage, this.chat);
- // 调用发送消息接口
this.$http({
url: "/message/private/send",
method: "post",
data: replyMsgInfo
}).then(res => {
- //console.log("自动回复发送成功", res);
-
- // 删除"正在输入"的临时消息
this.chatStore.deleteMessage(typingMessage, this.chat);
- // 消息会通过 WebSocket 推送过来,前端会自动接收并显示
- // 或者如果接口直接返回消息体,可以手动插入
if (res && res.id) {
let replyMessage = {
id: res.id,
@@ -409,51 +414,15 @@ export default {
}
}).catch(err => {
console.error("自动回复发送失败", err);
-
- // 删除"正在输入"的临时消息
this.chatStore.deleteMessage(typingMessage, this.chat);
- // 可选:显示发送失败提示
uni.showToast({
- title: "自动回复失败",
+ title: this.$t('chat.autoReplyFailed'),
icon: "none"
});
});
},
- // 新增方法:触发客服自动回复(调用后端接口)
- // triggerAutoReply(autoReply) {
- // // 构建客服回复消息,关键是要传入 sendId
- // let replyMsgInfo = {
- // sendId: this.chat.targetId, // 客服ID作为发送者
- // recvId: this.mine.id, // 当前用户作为接收者
- // type: autoReply.replyType, // 回复类型
- // };
- // //console.log(replyMsgInfo)
- // // 根据类型设置内容
- // if (autoReply.replyType === 0) {
- // // 文本回复
- // replyMsgInfo.content = autoReply.replyContent;
- // } else if (autoReply.replyType === 1) {
- // // 图片回复
- // replyMsgInfo.content = JSON.stringify({
- // originUrl: autoReply.replyContent,
- // thumbUrl: autoReply.replyContent,
- // });
- // }
-
- // // 调用发送消息接口
- // this.$http({
- // url: "/message/private/send",
- // method: "post",
- // data: replyMsgInfo
- // }).then(res => {
- // //console.log("自动回复发送成功", res);
- // // 消息会通过 WebSocket 推送过来,前端会自动接收并显示
- // }).catch(err => {
- // console.error("自动回复发送失败", err);
- // });
- // },
onRecorderInput() {
this.showRecord = true;
this.switchChatTabBox("none");
@@ -463,7 +432,6 @@ export default {
this.switchChatTabBox("none");
},
onSendRecord(data) {
- // 检查是否被封禁
if (this.isBanned) {
this.showBannedTip();
return;
@@ -474,26 +442,19 @@ export default {
type: this.$enums.MESSAGE_TYPE.AUDIO,
receipt: this.isReceipt,
};
- // 填充对方id
this.fillTargetId(msgInfo, this.chat.targetId);
- // 防止发送期间用户切换会话导致串扰
const chat = this.chat;
- // const chat = this.chatStore.chats[this.activeChatIdx];
if (!chat) return;
- // 消息回显
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();
this.sendMessageRequest(msgInfo)
.then((m) => {
- // 更新消息
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.id = m.id;
tmpMessage.status = m.status;
this.chatStore.updateMessage(tmpMessage, chat);
- // 会话置顶
this.moveChatToTop();
- // 滚动到底部
this.scrollToBottom();
this.isReceipt = false;
})
@@ -523,7 +484,6 @@ export default {
});
},
onGroupVideo() {
- // 邀请成员发起通话
let ids = [this.mine.id];
this.$refs.selBox.init(ids, ids, []);
this.$refs.selBox.open();
@@ -535,7 +495,6 @@ export default {
let users = [];
ids.forEach((id) => {
let m = this.groupMembers.find((m) => m.userId == id);
- // 只取部分字段,压缩url长度
users.push({
id: m.userId,
nickName: m.showNickName,
@@ -614,12 +573,12 @@ export default {
sendText = sendText.trim();
if (!sendText && this.atUserIds.length == 0) {
return uni.showToast({
- title: "不能发送空白信息",
+ title: this.$t('chat.cannotSendBlank'),
icon: "none",
});
}
- let receiptText = this.isReceipt ? "【回执消息】" : "";
+ let receiptText = this.isReceipt ? this.$t('chat.receiptMessage') : "";
let atText = this.createAtText();
let msgInfo = {
tmpId: this.generateId(),
@@ -636,16 +595,10 @@ export default {
const chat = this.chat;
if (!chat) return;
- // 创建临时消息
let tmpMessage = this.buildTmpMessage(msgInfo);
-
- // 保存临时消息的副本用于更新
const tmpMessageCopy = JSON.parse(JSON.stringify(tmpMessage));
-
- // 插入消息到 store
this.chatStore.insertMessage(tmpMessageCopy, chat);
- // 关键:使用 chat 引用来获取最新消息
const chatIndex = this.chatStore.chats.findIndex(
(c) => c.targetId === chat.targetId && c.type === chat.type
);
@@ -657,30 +610,22 @@ export default {
);
if (msgIndex !== -1) {
- // 滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
- // 发送请求
this.sendMessageRequest(msgInfo)
.then((m) => {
- // 更新消息 - 使用响应式更新
const updatedMessage = {
...targetChat.messages[msgIndex],
id: m.id,
status: m.status,
content: m.content,
};
-
- // 使用 Vue.set 或直接替换数组项
this.$set(targetChat.messages, msgIndex, updatedMessage);
-
- // 触发 chat 引用更新
this.$forceUpdate();
})
.catch(() => {
- // 更新失败状态
const failedMessage = {
...targetChat.messages[msgIndex],
status: this.$enums.MESSAGE_STATUS.FAILED,
@@ -689,7 +634,7 @@ export default {
this.$forceUpdate();
uni.showToast({
- title: "发送失败",
+ title: this.$t('chat.sendFailed'),
icon: "none",
});
});
@@ -702,7 +647,7 @@ export default {
let atText = "";
this.atUserIds.forEach((id) => {
if (id == -1) {
- atText += ` @全体成员`;
+ atText += ` @${this.$t('chat.atAll')}`;
} else {
let member = this.groupMembers.find((m) => m.userId == id);
if (member) {
@@ -726,12 +671,9 @@ export default {
}
},
scrollToMsgIdx(idx) {
- // 如果scrollMsgIdx值没变化,滚动条不会移动
if (idx == this.scrollMsgIdx && idx > 0) {
this.$nextTick(() => {
- // 先滚动到上一条
this.scrollMsgIdx = idx - 1;
- // 再滚动目标位置
this.scrollToMsgIdx(idx);
});
return;
@@ -759,7 +701,6 @@ export default {
},
selectEmoji(emoText) {
let path = this.$emo.textToPath(emoText);
- // 先把键盘禁用了,否则会重新弹出键盘
this.isReadOnly = true;
this.isEmpty = false;
this.$nextTick(() => {
@@ -776,7 +717,6 @@ export default {
});
},
onUploadImageBefore(file) {
- // 检查是否被封禁
if (this.isBanned) {
this.showBannedTip();
return;
@@ -796,16 +736,11 @@ export default {
readedCount: 0,
status: this.$enums.MESSAGE_STATUS.SENDING,
};
- // 填充对方id
this.fillTargetId(msgInfo, this.chat.targetId);
- // 插入消息
this.chatStore.insertMessage(msgInfo, this.chat);
- // 会话置顶
this.moveChatToTop();
- // 借助file对象保存
file.msgInfo = msgInfo;
file.chat = this.chat;
- // 更新图片宽高
let chat = this.chat;
this.getImageSize(file).then((size) => {
msgInfo = JSON.parse(JSON.stringify(msgInfo));
@@ -839,7 +774,6 @@ export default {
this.chatStore.updateMessage(msgInfo, file.chat);
},
onUploadFileBefore(file) {
- // 检查是否被封禁
if (this.isBanned) {
this.showBannedTip();
return;
@@ -859,13 +793,9 @@ export default {
readedCount: 0,
status: this.$enums.MESSAGE_STATUS.SENDING,
};
- // 填充对方id
this.fillTargetId(msgInfo, this.chat.targetId);
- // 插入消息
this.chatStore.insertMessage(msgInfo, this.chat);
- // 会话置顶
this.moveChatToTop();
- // 借助file对象保存
file.msgInfo = msgInfo;
file.chat = this.chat;
return true;
@@ -899,25 +829,20 @@ export default {
onResendMessage(msgInfo) {
if (msgInfo.type != this.$enums.MESSAGE_TYPE.TEXT) {
uni.showToast({
- title: "该消息不支持自动重新发送,建议手动重新发送",
+ title: this.$t('chat.resendNotSupported'),
icon: "none",
});
return;
}
- // 防止发送期间用户切换会话导致串扰
const chat = this.chat;
- // const chat = this.chatStore.chats[this.activeChatIdx];
if (!chat) return;
- // 删除旧消息
this.chatStore.deleteMessage(msgInfo, chat);
- // 重新发送
msgInfo.tmpId = this.generateId();
let tmpMessage = this.buildTmpMessage(msgInfo);
this.chatStore.insertMessage(tmpMessage, chat);
this.moveChatToTop();
this.sendMessageRequest(msgInfo)
.then((m) => {
- // 更新消息
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.id = m.id;
tmpMessage.status = m.status;
@@ -925,7 +850,6 @@ export default {
this.chatStore.updateMessage(tmpMessage, chat);
})
.catch(() => {
- // 更新消息
tmpMessage = JSON.parse(JSON.stringify(tmpMessage));
tmpMessage.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.updateMessage(tmpMessage, chat);
@@ -934,13 +858,15 @@ export default {
onDeleteMessage(msgInfo) {
uni.showModal({
- title: "删除消息",
- content: "确认删除消息?",
+ title: this.$t('chat.deleteMessage'),
+ content: this.$t('chat.confirmDelete'),
+ confirmText: this.$t('common.confirm'),
+ cancelText: this.$t('common.cancel'),
success: (res) => {
if (!res.cancel) {
this.chatStore.deleteMessage(msgInfo, this.chat);
uni.showToast({
- title: "删除成功",
+ title: this.$t('chat.deleteSuccess'),
icon: "none",
});
}
@@ -949,8 +875,10 @@ export default {
},
onRecallMessage(msgInfo) {
uni.showModal({
- title: "撤回消息",
- content: "确认撤回消息?",
+ title: this.$t('chat.recallMessage'),
+ content: this.$t('chat.confirmRecall'),
+ confirmText: this.$t('common.confirm'),
+ cancelText: this.$t('common.cancel'),
success: (res) => {
if (!res.cancel) {
let url = `/message/${this.chat.type.toLowerCase()}/recall/${
@@ -971,10 +899,10 @@ export default {
uni.setClipboardData({
data: msgInfo.content,
success: () => {
- uni.showToast({ title: "复制成功" });
+ uni.showToast({ title: this.$t('chat.copySuccess') });
},
fail: () => {
- uni.showToast({ title: "复制失败", icon: "none" });
+ uni.showToast({ title: this.$t('chat.copyFailed'), icon: "none" });
},
});
},
@@ -993,23 +921,15 @@ export default {
},
fail(e) {
uni.showToast({
- title: "文件下载失败",
+ title: this.$t('chat.downloadFailed'),
icon: "none",
});
},
});
},
onClickToBottom() {
- /**
- * 有些ios设备在点击回到底部后页面会卡住,原因不详
- * 解决方式: 延迟300ms再滚动
- */
setTimeout(() => {
this.scrollToBottom();
- /**
- * 有些设备滚到底部时会莫名触发滚动到顶部的事件
- * 解决方式: 延迟100ms保证能准确设置底部标志
- */
setTimeout(() => {
this.isInBottom = true;
this.newMessageSize = 0;
@@ -1017,42 +937,32 @@ export default {
}, 300);
},
onScroll(e) {
- // 记录当前滚动条高度
this.scrollViewHeight = e.detail.scrollHeight;
},
onScrollToTop() {
if (this.showMinIdx > 0) {
- // 防止滚动条定格在顶部,不能一直往上滚
if (uni.getSystemInfoSync().platform == "ios") {
- // ios采用scroll-top定位,否则可能会出现白屏
this.holdingScrollBar(this.scrollViewHeight);
} else {
- // 安卓采用scroll-into-view定位
this.scrollToMsgIdx(this.showMinIdx);
}
- // 多展示20条信息
this.showMinIdx = this.showMinIdx > 20 ? this.showMinIdx - 20 : 0;
}
- // 清除底部标识
this.isInBottom = false;
},
onScrollToBottom(e) {
- // 设置底部标识
this.isInBottom = true;
this.newMessageSize = 0;
},
holdingScrollBar(scrollViewHeight) {
- // 内容高度
const query = uni.createSelectorQuery().in(this);
setTimeout(() => {
query.select(".chat-wrap").boundingClientRect();
query.exec((data) => {
this.scrollTop = data[0].height - scrollViewHeight;
if (this.scrollTop < 10) {
- // 未渲染完成,重试一次
this.holdingScrollBar();
} else {
- // 让页面再滚动一下,解决部分ios手机出现白屏问题
const delays = [100, 300, 1000];
delays.forEach((delay) =>
setTimeout(() => (this.scrollTop += 5), delay)
@@ -1083,21 +993,14 @@ export default {
.context((res) => {
this.editorCtx = res.context;
// #ifdef H5
- // 使用全局变量确保只添加一次监听器
if (!window.__pasteHandlerAdded__) {
window.__pasteHandlerAdded__ = true;
- // 初始化全局变量
if (window.__isPasting__ === undefined) {
window.__isPasting__ = false;
}
- //console.log('[chat-box] 首次添加粘贴事件监听器');
-
- // 移除所有可能的旧监听器
if (this.pasteHandler) {
document.removeEventListener('paste', this.pasteHandler);
}
-
- // 使用bind绑定this,确保this指向正确
this.pasteHandler = this.onDocumentPaste.bind(this);
document.addEventListener('paste', this.pasteHandler);
}
@@ -1117,9 +1020,6 @@ export default {
async onDocumentPaste(e) {
// #ifdef H5
const timestamp = new Date().toISOString();
- //console.log(`[${timestamp}] 粘贴事件触发, isPasting: ${window.__isPasting__}`);
-
- // 检查是否在editor中粘贴
const activeElement = document.activeElement;
const isInEditor = activeElement && (
activeElement.closest('#editor') ||
@@ -1127,13 +1027,10 @@ export default {
);
if (!isInEditor) {
- //console.log(`[${timestamp}] 不在editor中,忽略`);
return;
}
- // 使用全局变量防止重复触发
if (window.__isPasting__) {
- //console.log(`[${timestamp}] 全局isPasting为true,忽略重复触发`);
e.preventDefault();
e.stopPropagation();
return;
@@ -1145,7 +1042,6 @@ export default {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === 'file' && item.type.startsWith('image/')) {
- //console.log(`[${timestamp}] 检测到图片,开始处理`);
e.preventDefault();
e.stopPropagation();
hasImage = true;
@@ -1153,50 +1049,36 @@ export default {
try {
const file = item.getAsFile();
-
- // 将文件转换为base64
const base64 = await this.fileToBase64(file);
- //console.log(`[${timestamp}] base64转换完成,长度: ${base64.length}`);
-
- // 创建临时文件对象
const tempFile = {
path: base64,
uid: this.generateId(),
size: file.size,
name: file.name || 'pasted-image.png'
};
-
- // 调用现有的图片上传逻辑
await this.handlePasteImage(tempFile);
} catch (error) {
- //console.log(`[${timestamp}] 处理粘贴图片失败:`, error);
+ console.error("处理粘贴图片失败:", error);
} finally {
window.__isPasting__ = false;
}
-
break;
}
}
- // 如果没有图片,立即重置标志
if (!hasImage) {
- //console.log(`[${timestamp}] 没有检测到图片`);
this.isPasting = false;
}
// #endif
},
async fileToBase64(file) {
- const timestamp = new Date().toISOString();
- //console.log(`[${timestamp}] fileToBase64被调用,文件大小:`, file.size);
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
- //console.log(`[${timestamp}] fileToBase64 onload,结果长度:`, event.target.result.length);
resolve(event.target.result);
};
reader.onerror = (error) => {
- //console.log(`[${timestamp}] fileToBase64 onerror:`, error);
reject(error);
};
reader.readAsDataURL(file);
@@ -1204,17 +1086,11 @@ export default {
},
async handlePasteImage(file) {
- const timestamp = new Date().toISOString();
- //console.log(`[${timestamp}] handlePasteImage被调用, isPasting: ${window.__isPasting__}, chat:`, this.chat);
-
- // 检查chat是否存在
if (!this.chat) {
- //console.log(`[${timestamp}] handlePasteImage: chat为空,忽略`);
return;
}
try {
- // 检查是否被封禁
if (this.isBanned) {
this.showBannedTip();
return;
@@ -1237,20 +1113,12 @@ export default {
status: this.$enums.MESSAGE_STATUS.SENDING,
};
- // 填充对方id
this.fillTargetId(msgInfo, this.chat.targetId);
-
- // 插入消息
this.chatStore.insertMessage(msgInfo, this.chat);
-
- // 会话置顶
this.moveChatToTop();
-
- // 借助file对象保存
file.msgInfo = msgInfo;
file.chat = this.chat;
- // 更新图片宽高
const size = await this.getImageSize(file);
msgInfo = JSON.parse(JSON.stringify(msgInfo));
data.width = size.width;
@@ -1259,22 +1127,17 @@ export default {
this.chatStore.updateMessage(msgInfo, this.chat);
this.scrollToBottom();
- // 上传图片到服务器
await this.uploadPastedImage(file);
} catch (error) {
- //console.log(`[${timestamp}] 处理粘贴图片失败:`, error);
+ console.error("处理粘贴图片失败:", error);
this.isPasting = false;
}
},
async uploadPastedImage(file) {
- const timestamp = new Date().toISOString();
- //console.log(`[${timestamp}] uploadPastedImage被调用`);
-
// #ifdef H5
try {
- // 将base64转换为Blob
const base64Data = file.path.split(',')[1];
const byteCharacters = atob(base64Data);
const byteArrays = [];
@@ -1289,51 +1152,40 @@ export default {
}
const blob = new Blob(byteArrays, { type: 'image/png' });
- // 创建FormData
const formData = new FormData();
formData.append('file', blob, file.name);
- //console.log(`[${timestamp}] 开始上传图片`);
-
- // 使用fetch上传
const response = await fetch(UNI_APP.BASE_URL + `/image/upload?isPermanent=false&thumbSize=50`, {
method: 'POST',
body: formData
});
const data = await response.json();
- //console.log(`[${timestamp}] 上传响应:`, data);
if (data.code === 200) {
- //console.log(`[${timestamp}] 上传成功,准备发送消息`);
- // 上传成功,更新消息内容
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.content = JSON.stringify(data.data);
msgInfo.receipt = this.isReceipt;
this.chatStore.updateMessage(msgInfo, file.chat);
- // 发送消息
const m = await this.sendMessageRequest(msgInfo);
- //console.log(`[${timestamp}] 消息发送成功`);
msgInfo.id = m.id;
msgInfo.status = m.status;
this.isReceipt = false;
this.chatStore.updateMessage(msgInfo, file.chat);
} else {
- //console.log(`[${timestamp}] 上传失败:`, data);
uni.showToast({
icon: "none",
- title: data.message || "上传失败",
+ title: data.message || this.$t('chat.uploadFailed'),
});
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
this.chatStore.updateMessage(msgInfo, file.chat);
}
} catch (error) {
- //console.log(`[${timestamp}] 上传异常:`, error);
uni.showToast({
icon: "none",
- title: "上传失败",
+ title: this.$t('chat.uploadFailed'),
});
let msgInfo = JSON.parse(JSON.stringify(file.msgInfo));
msgInfo.status = this.$enums.MESSAGE_STATUS.FAILED;
@@ -1344,9 +1196,7 @@ export default {
onAudioStateChange(state, msgInfo) {
const playingAudio = this.$refs["message" + msgInfo.id][0];
if (state == "PLAYING" && playingAudio != this.playingAudio) {
- // 停止之前的录音
this.playingAudio && this.playingAudio.stopPlayAudio();
- // 记录当前正在播放的消息
this.playingAudio = playingAudio;
}
},
@@ -1395,23 +1245,19 @@ export default {
},
updateFriendInfo() {
if (this.isFriend) {
- // store的数据不能直接修改,深拷贝一份store的数据
let friend = JSON.parse(JSON.stringify(this.friend));
friend.headImage = this.userInfo.headImageThumb;
friend.nickName = this.userInfo.nickName;
friend.showNickName = friend.remarkNickName
? friend.remarkNickName
: friend.nickName;
- // 更新好友列表中的昵称和头像
this.friendStore.updateFriend(friend);
- // 更新会话中的头像和昵称
this.chatStore.updateChatFromFriend(friend);
} else {
this.chatStore.updateChatFromUser(this.userInfo);
}
},
loadFriend(friendId) {
- // 获取好友用户信息
this.$http({
url: `/user/find/${friendId}`,
method: "GET",
@@ -1421,15 +1267,12 @@ export default {
});
},
rpxTopx(rpx) {
- // rpx转换成px
let info = uni.getSystemInfoSync();
let px = (info.windowWidth * rpx) / 750;
return Math.floor(rpx);
},
sendMessageRequest(msgInfo) {
return new Promise((resolve, reject) => {
- // 请求入队列,防止请求"后发先至",导致消息错序
-
this.reqQueue.push({ msgInfo, resolve, reject });
this.processReqQueue();
});
@@ -1438,7 +1281,6 @@ export default {
if (this.reqQueue.length && !this.isSending) {
this.isSending = true;
const reqData = this.reqQueue.shift();
- //console.log(reqData.msgInfo);
this.$http({
url: this.messageAction,
method: "post",
@@ -1452,7 +1294,6 @@ export default {
})
.finally(() => {
this.isSending = false;
- // 发送下一条请求
this.processReqQueue();
});
}
@@ -1460,23 +1301,17 @@ export default {
reCalChatMainHeight() {
let sysInfo = uni.getSystemInfoSync();
let h = this.windowHeight;
- // 减去标题栏高度
h -= 50;
- // 减去键盘高度
if (this.isShowKeyBoard || this.chatTabBox != "none") {
h -= this.keyboardHeight;
this.scrollToBottom();
}
-
- // APP需要减去状态栏高度
// #ifdef APP-PLUS
h -= sysInfo.statusBarHeight;
// #endif
this.chatMainHeight = h;
// #ifndef APP
- // ios浏览器键盘把页面顶起后,页面长度不会变化,这里把页面拉到顶部适配一下
if (uni.getSystemInfoSync().platform == "ios") {
- // 不同手机需要的延时时间不一致,采用分段延时的方式处理
const delays = [50, 100, 500];
delays.forEach((delay) => {
setTimeout(() => {
@@ -1492,26 +1327,20 @@ export default {
listenKeyBoard() {
// #ifdef H5
if (navigator.platform == "Win32") {
- // 电脑端不需要弹出键盘
- //console.log("navigator.platform:", navigator.platform);
return;
}
if (uni.getSystemInfoSync().platform == "ios") {
- // 监听键盘高度,ios13以上开始支持
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", this.resizeListener);
} else {
- // ios h5实现键盘监听
window.addEventListener("focusin", this.focusInListener);
window.addEventListener("focusout", this.focusOutListener);
}
} else {
- // 安卓h5实现键盘监听
window.addEventListener("resize", this.resizeListener);
}
// #endif
// #ifndef H5
- // app实现键盘监听
uni.onKeyboardHeightChange(this.keyBoardListener);
// #endif
},
@@ -1534,9 +1363,8 @@ export default {
keyBoardListener(res) {
this.isShowKeyBoard = res.height > 0;
if (this.isShowKeyBoard) {
- this.keyboardHeight = res.height; // 获取并保存键盘高度
+ this.keyboardHeight = res.height;
// #ifdef APP-PLUS
- // ios app的键盘高度不准,需要减去屏幕和窗口差
let sysInfo = uni.getSystemInfoSync();
if (sysInfo.platform == "ios") {
this.keyboardHeight -= this.screenHeight - this.windowHeight;
@@ -1547,7 +1375,6 @@ export default {
},
resizeListener() {
let keyboardHeight = this.initHeight - window.innerHeight;
- // 兼容部分ios浏览器
if (window.visualViewport && uni.getSystemInfoSync().platform == "ios") {
keyboardHeight = this.initHeight - window.visualViewport.height;
}
@@ -1577,10 +1404,10 @@ export default {
};
if (this.isPrivate) {
msgInfo.recvId = this.mine.id;
- msgInfo.content = "该用户已被管理员封禁,原因:" + this.userInfo.reason;
+ msgInfo.content = this.$t('chat.userBanned', { reason: this.userInfo.reason });
} else {
msgInfo.groupId = this.group.id;
- msgInfo.content = "本群聊已被管理员封禁,原因:" + this.group.reason;
+ msgInfo.content = this.$t('chat.groupBanned', { reason: this.group.reason });
}
this.chatStore.insertMessage(msgInfo, this.chat);
},
@@ -1611,7 +1438,6 @@ export default {
generateId() {
const id =
String(new Date().getTime()) + String(Math.floor(Math.random() * 1000));
- // 必须保证id是递增
if (this.maxTmpId > id) {
return this.generateId();
}
@@ -1619,21 +1445,20 @@ export default {
return id;
},
- //获取后台设置
async getSetting() {
this.$http({
url: "/getSetting",
method: "post",
}).then((res) => {
- if(res.isFileOpen === 1){
- this.isFileOpen = true;
- }
+ if(res.isFileOpen === 1){
+ this.isFileOpen = true;
+ }
});
},
},
computed: {
title() {
- if (!this.chat) return "";
+ if (!this.chat) return this.$t('chat.title');
let title = this.chat.showName;
if (this.isGroup) {
let size = this.groupMembers.filter((m) => !m.quit).length;
@@ -1694,7 +1519,7 @@ export default {
if (id == -1) {
atUsers.push({
id: -1,
- showNickName: "全体成员",
+ showNickName: this.$t('chat.atAll'),
});
return;
}
@@ -1722,7 +1547,6 @@ export default {
chat: {
handler(newChat, oldChat) {
if (newChat && newChat.messages && newChat.messages.length > 0) {
- // 当消息数量变化时,如果正在底部则滚动
if (this.isInBottom) {
this.$nextTick(() => {
this.scrollToBottom();
@@ -1755,14 +1579,12 @@ export default {
unreadCount: {
handler(newCount, oldCount) {
if (newCount > 0) {
- // 消息已读
this.readedMessage();
}
},
},
loading: {
handler(newLoading, oldLoading) {
- // 断线重连后,需要更新一下已读状态
if (!newLoading && this.isPrivate) {
this.loadReaded(this.chat.targetId);
}
@@ -1771,7 +1593,8 @@ export default {
},
async onLoad(options) {
try {
- uni.showLoading({ title: "加载中...", mask: true });
+ this.currentLang = uni.getStorageSync("app_language") || "zh";
+ uni.showLoading({ title: this.$t('common.loading'), mask: true });
let targetId = null;
let type = "PRIVATE";
@@ -1783,7 +1606,10 @@ export default {
if (this.friendStore.friends.length === 0)
await this.friendStore.loadFriend();
const first = this.friendStore.friends[0];
- if (!first) return uni.showToast({ title: "暂无好友", icon: "none" });
+ if (!first) {
+ uni.hideLoading();
+ return uni.showToast({ title: this.$t('chat.noFriends'), icon: "none" });
+ }
targetId = first.id;
}
@@ -1799,7 +1625,7 @@ export default {
chat = {
targetId,
type,
- showName: friend?.nickName || "客服",
+ showName: friend?.nickName || this.$t('common.customerService'),
headImage: friend?.headImage || "",
isDnd: false,
lastContent: "",
@@ -1821,11 +1647,8 @@ export default {
}
this.readedMessage();
-
this.loadFriend(targetId);
this.loadReaded(targetId);
-
- // 加载常见问题
this.loadCommonQuestions(targetId);
this.listenKeyBoard();
@@ -1833,7 +1656,6 @@ export default {
this.screenHeight = uni.getSystemInfoSync().screenHeight;
this.reCalChatMainHeight();
- //获取配置信息
await this.getSetting();
this.$nextTick(() => this.scrollToBottom());
} catch (err) {
@@ -1845,7 +1667,6 @@ export default {
onUnload() {
this.unListenKeyboard();
// #ifdef H5
- // 移除粘贴事件监听
if (this.hasPasteListener && this.pasteHandler) {
document.removeEventListener('paste', this.pasteHandler);
this.hasPasteListener = false;
@@ -1857,6 +1678,76 @@ export default {
+
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/ara.json b/im-uniapp/static/i18n/ara.json
new file mode 100644
index 0000000..b48cbf2
--- /dev/null
+++ b/im-uniapp/static/i18n/ara.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "نسخ",
+ "recall": "تراجع",
+ "delete": "حذف",
+ "download": "تحميل",
+ "read": "مقروء",
+ "unread": "غير مقروء",
+ "readedCount": "{count} شخص قرأ",
+ "guessWantAsk": "ربما تريد السؤال",
+ "typing": "يكتب...",
+ "autoReplyFailed": "فشل الرد التلقائي",
+ "receiptMessage": "【مقروء】",
+ "inputPlaceholder": "اكتب رسالة",
+ "send": "إرسال",
+ "newMessages": "{count} رسائل جديدة",
+ "cannotSendBlank": "لا ترسل رسالة فارغة",
+ "copySuccess": "تم النسخ",
+ "copyFailed": "فشل النسخ",
+ "downloadFailed": "فشل التحميل",
+ "deleteMessage": "حذف الرسالة",
+ "confirmDelete": "حذف هذه الرسالة؟",
+ "recallMessage": "تراجع الرسالة",
+ "confirmRecall": "تراجع هذه الرسالة؟",
+ "deleteSuccess": "تم الحذف",
+ "resendNotSupported": "غير مدعوم",
+ "sendFailed": "فشل الإرسال",
+ "uploadFailed": "فشل الرفع",
+ "userBanned": "مكتوم: {reason}",
+ "groupBanned": "مكتوم في المجموعة: {reason}",
+ "atAll": "الكل",
+ "file": "ملف",
+ "album": "ألبوم",
+ "camera": "كاميرا",
+ "noFriends": "لا أصدقاء",
+ "title": "دردشة"
+ },
+ "common": {
+ "confirm": "موافق",
+ "cancel": "إلغاء",
+ "loading": "جاري التحميل...",
+ "justNow": "الآن",
+ "minutesAgo": "دقائق مضت",
+ "yesterday": "أمس"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/de.json b/im-uniapp/static/i18n/de.json
new file mode 100644
index 0000000..681ff31
--- /dev/null
+++ b/im-uniapp/static/i18n/de.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "Kopieren",
+ "recall": "Zurücknehmen",
+ "delete": "Löschen",
+ "download": "Herunterladen",
+ "read": "Gelesen",
+ "unread": "Ungelesen",
+ "readedCount": "{count} Personen gelesen",
+ "guessWantAsk": "Vielleicht fragen Sie",
+ "typing": "Schreibt...",
+ "autoReplyFailed": "Automatische Antwort fehlgeschlagen",
+ "receiptMessage": "【Gelesen】",
+ "inputPlaceholder": "Nachricht eingeben",
+ "send": "Senden",
+ "newMessages": "{count} neue Nachrichten",
+ "cannotSendBlank": "Keine leeren Nachrichten",
+ "copySuccess": "Kopiert",
+ "copyFailed": "Kopieren fehlgeschlagen",
+ "downloadFailed": "Download fehlgeschlagen",
+ "deleteMessage": "Nachricht löschen",
+ "confirmDelete": "Diese Nachricht löschen?",
+ "recallMessage": "Nachricht zurücknehmen",
+ "confirmRecall": "Diese Nachricht zurücknehmen?",
+ "deleteSuccess": "Gelöscht",
+ "resendNotSupported": "Nicht unterstützt",
+ "sendFailed": "Senden fehlgeschlagen",
+ "uploadFailed": "Hochladen fehlgeschlagen",
+ "userBanned": "Stumm geschaltet: {reason}",
+ "groupBanned": "In Gruppe stumm: {reason}",
+ "atAll": "Alle",
+ "file": "Datei",
+ "album": "Album",
+ "camera": "Kamera",
+ "noFriends": "Keine Freunde",
+ "title": "Chat"
+ },
+ "common": {
+ "confirm": "OK",
+ "cancel": "Abbrechen",
+ "loading": "Lädt...",
+ "justNow": "Gerade eben",
+ "minutesAgo": "Min. vor",
+ "yesterday": "Gestern"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/en.json b/im-uniapp/static/i18n/en.json
new file mode 100644
index 0000000..27bf543
--- /dev/null
+++ b/im-uniapp/static/i18n/en.json
@@ -0,0 +1,47 @@
+{
+ "chat": {
+ "copy": "Copy",
+ "recall": "Recall",
+ "delete": "Delete",
+ "download": "Download & Open",
+ "read": "Read",
+ "unread": "Unread",
+ "readedCount": "{count} read",
+
+ "guessWantAsk": "You may want to ask",
+ "typing": "typing...",
+ "autoReplyFailed": "Auto reply failed",
+ "receiptMessage": "[Read Receipt]",
+ "inputPlaceholder": "Type a message",
+ "send": "Send",
+ "newMessages": "{count} new messages",
+ "cannotSendBlank": "Cannot send empty message",
+ "copySuccess": "Copied",
+ "copyFailed": "Copy failed",
+ "downloadFailed": "Download failed",
+ "deleteMessage": "Delete Message",
+ "confirmDelete": "Sure to delete?",
+ "recallMessage": "Recall Message",
+ "confirmRecall": "Sure to recall?",
+ "deleteSuccess": "Deleted",
+ "resendNotSupported": "Resend not supported",
+ "sendFailed": "Send failed",
+ "uploadFailed": "Upload failed",
+ "userBanned": "You are banned: {reason}",
+ "groupBanned": "You are banned in group: {reason}",
+ "atAll": "All",
+ "file": "File",
+ "album": "Album",
+ "camera": "Camera",
+ "noFriends": "No friends",
+ "title": "Chat"
+ },
+ "common": {
+ "confirm": "OK",
+ "cancel": "Cancel",
+ "loading": "Loading...",
+ "justNow": "Just now",
+ "minutesAgo": "minutes ago",
+ "yesterday": "Yesterday"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/fra.json b/im-uniapp/static/i18n/fra.json
new file mode 100644
index 0000000..628ebea
--- /dev/null
+++ b/im-uniapp/static/i18n/fra.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "Copier",
+ "recall": "Rappeler",
+ "delete": "Supprimer",
+ "download": "Télécharger",
+ "read": "Lu",
+ "unread": "Non lu",
+ "readedCount": "{count} personnes lues",
+ "guessWantAsk": "Vous pouvez demander",
+ "typing": "En train d'écrire...",
+ "autoReplyFailed": "Échec de la réponse auto",
+ "receiptMessage": "【Lu】",
+ "inputPlaceholder": "Entrez un message",
+ "send": "Envoyer",
+ "newMessages": "{count} nouveaux messages",
+ "cannotSendBlank": "Pas de message vide",
+ "copySuccess": "Copié",
+ "copyFailed": "Échec de la copie",
+ "downloadFailed": "Échec du téléchargement",
+ "deleteMessage": "Supprimer le message",
+ "confirmDelete": "Supprimer ce message?",
+ "recallMessage": "Rappeler le message",
+ "confirmRecall": "Rappeler ce message?",
+ "deleteSuccess": "Supprimé",
+ "resendNotSupported": "Non supporté",
+ "sendFailed": "Échec de l'envoi",
+ "uploadFailed": "Échec du téléversement",
+ "userBanned": "Muet: {reason}",
+ "groupBanned": "Groupe muet: {reason}",
+ "atAll": "Tous",
+ "file": "Fichier",
+ "album": "Album",
+ "camera": "Caméra",
+ "noFriends": "Pas d'amis",
+ "title": "Discussion"
+ },
+ "common": {
+ "confirm": "OK",
+ "cancel": "Annuler",
+ "loading": "Chargement...",
+ "justNow": "À l'instant",
+ "minutesAgo": "min avant",
+ "yesterday": "Hier"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/jp.json b/im-uniapp/static/i18n/jp.json
new file mode 100644
index 0000000..ca82e0b
--- /dev/null
+++ b/im-uniapp/static/i18n/jp.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "コピー",
+ "recall": "取り消し",
+ "delete": "削除",
+ "download": "ダウンロードして開く",
+ "read": "既読",
+ "unread": "未読",
+ "readedCount": "{count}人が既読",
+ "guessWantAsk": "質問したいこと",
+ "typing": "入力中...",
+ "autoReplyFailed": "自動返信失敗",
+ "receiptMessage": "【既読確認】",
+ "inputPlaceholder": "メッセージを入力",
+ "send": "送信",
+ "newMessages": "{count}件の新着メッセージ",
+ "cannotSendBlank": "空白メッセージは送信できません",
+ "copySuccess": "コピー成功",
+ "copyFailed": "コピー失敗",
+ "downloadFailed": "ダウンロード失敗",
+ "deleteMessage": "メッセージ削除",
+ "confirmDelete": "このメッセージを削除しますか?",
+ "recallMessage": "メッセージ取り消し",
+ "confirmRecall": "このメッセージを取り消しますか?",
+ "deleteSuccess": "削除成功",
+ "resendNotSupported": "再送信に対応していません",
+ "sendFailed": "送信失敗",
+ "uploadFailed": "アップロード失敗",
+ "userBanned": "発言禁止されています: {reason}",
+ "groupBanned": "グループで発言禁止: {reason}",
+ "atAll": "全員",
+ "file": "ファイル",
+ "album": "アルバム",
+ "camera": "カメラ",
+ "noFriends": "友達なし",
+ "title": "チャット"
+ },
+ "common": {
+ "confirm": "確定",
+ "cancel": "キャンセル",
+ "loading": "読み込み中...",
+ "justNow": "たった今",
+ "minutesAgo": "分前",
+ "yesterday": "昨日"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/kor.json b/im-uniapp/static/i18n/kor.json
new file mode 100644
index 0000000..95dd003
--- /dev/null
+++ b/im-uniapp/static/i18n/kor.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "복사",
+ "recall": "취소",
+ "delete": "삭제",
+ "download": "다운로드 및 열기",
+ "read": "읽음",
+ "unread": "안읽음",
+ "readedCount": "{count}명 읽음",
+ "guessWantAsk": "물어보고 싶은 것",
+ "typing": "입력 중...",
+ "autoReplyFailed": "자동 회신 실패",
+ "receiptMessage": "【읽음 확인】",
+ "inputPlaceholder": "메시지 입력",
+ "send": "전송",
+ "newMessages": "{count}개의 새 메시지",
+ "cannotSendBlank": "빈 메시지는 보낼 수 없습니다",
+ "copySuccess": "복사 성공",
+ "copyFailed": "복사 실패",
+ "downloadFailed": "다운로드 실패",
+ "deleteMessage": "메시지 삭제",
+ "confirmDelete": "이 메시지를 삭제할까요?",
+ "recallMessage": "메시지 취소",
+ "confirmRecall": "이 메시지를 취소할까요?",
+ "deleteSuccess": "삭제 성공",
+ "resendNotSupported": "재전송 지원 안됨",
+ "sendFailed": "전송 실패",
+ "uploadFailed": "업로드 실패",
+ "userBanned": "채팅 금지: {reason}",
+ "groupBanned": "그룹 채팅 금지: {reason}",
+ "atAll": "모두",
+ "file": "파일",
+ "album": "앨범",
+ "camera": "카메라",
+ "noFriends": "친구 없음",
+ "title": "채팅"
+ },
+ "common": {
+ "confirm": "확인",
+ "cancel": "취소",
+ "loading": "로딩 중...",
+ "justNow": "방금",
+ "minutesAgo": "분 전",
+ "yesterday": "어제"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/pt.json b/im-uniapp/static/i18n/pt.json
new file mode 100644
index 0000000..6dcd65a
--- /dev/null
+++ b/im-uniapp/static/i18n/pt.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "Copiar",
+ "recall": "Retirar",
+ "delete": "Excluir",
+ "download": "Baixar",
+ "read": "Lido",
+ "unread": "Não lido",
+ "readedCount": "{count} pessoas leram",
+ "guessWantAsk": "Talvez queira perguntar",
+ "typing": "Digitando...",
+ "autoReplyFailed": "Resposta automática falhou",
+ "receiptMessage": "【Lido】",
+ "inputPlaceholder": "Digite uma mensagem",
+ "send": "Enviar",
+ "newMessages": "{count} novas mensagens",
+ "cannotSendBlank": "Não envie mensagem vazia",
+ "copySuccess": "Copiado",
+ "copyFailed": "Falha ao copiar",
+ "downloadFailed": "Falha no download",
+ "deleteMessage": "Excluir mensagem",
+ "confirmDelete": "Excluir esta mensagem?",
+ "recallMessage": "Retirar mensagem",
+ "confirmRecall": "Retirar esta mensagem?",
+ "deleteSuccess": "Excluído",
+ "resendNotSupported": "Não suportado",
+ "sendFailed": "Falha no envio",
+ "uploadFailed": "Falha no upload",
+ "userBanned": "Silenciado: {reason}",
+ "groupBanned": "Grupo silenciado: {reason}",
+ "atAll": "Todos",
+ "file": "Arquivo",
+ "album": "Álbum",
+ "camera": "Câmera",
+ "noFriends": "Sem amigos",
+ "title": "Chat"
+ },
+ "common": {
+ "confirm": "OK",
+ "cancel": "Cancelar",
+ "loading": "Carregando...",
+ "justNow": "Agora mesmo",
+ "minutesAgo": "min atrás",
+ "yesterday": "Ontem"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/ru.json b/im-uniapp/static/i18n/ru.json
new file mode 100644
index 0000000..7b58f6c
--- /dev/null
+++ b/im-uniapp/static/i18n/ru.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "Копировать",
+ "recall": "Отозвать",
+ "delete": "Удалить",
+ "download": "Скачать и открыть",
+ "read": "Прочитано",
+ "unread": "Не прочитано",
+ "readedCount": "{count} человек прочитало",
+ "guessWantAsk": "Возможно, вы хотите спросить",
+ "typing": "Печатает...",
+ "autoReplyFailed": "Автоответ не удался",
+ "receiptMessage": "【Прочитано】",
+ "inputPlaceholder": "Введите сообщение",
+ "send": "Отправить",
+ "newMessages": "{count} новых сообщений",
+ "cannotSendBlank": "Нельзя отправить пустое",
+ "copySuccess": "Скопировано",
+ "copyFailed": "Ошибка копирования",
+ "downloadFailed": "Ошибка загрузки",
+ "deleteMessage": "Удалить сообщение",
+ "confirmDelete": "Удалить это сообщение?",
+ "recallMessage": "Отозвать сообщение",
+ "confirmRecall": "Отозвать это сообщение?",
+ "deleteSuccess": "Удалено",
+ "resendNotSupported": "Не поддерживается",
+ "sendFailed": "Ошибка отправки",
+ "uploadFailed": "Ошибка загрузки",
+ "userBanned": "Вы заблокированы: {reason}",
+ "groupBanned": "В группе блокировка: {reason}",
+ "atAll": "Все",
+ "file": "Файл",
+ "album": "Альбом",
+ "camera": "Камера",
+ "noFriends": "Нет друзей",
+ "title": "Чат"
+ },
+ "common": {
+ "confirm": "ОК",
+ "cancel": "Отмена",
+ "loading": "Загрузка...",
+ "justNow": "Только что",
+ "minutesAgo": "мин назад",
+ "yesterday": "Вчера"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/vie.json b/im-uniapp/static/i18n/vie.json
new file mode 100644
index 0000000..b930954
--- /dev/null
+++ b/im-uniapp/static/i18n/vie.json
@@ -0,0 +1,46 @@
+{
+ "chat": {
+ "copy": "Sao chép",
+ "recall": "Thu hồi",
+ "delete": "Xóa",
+ "download": "Tải & mở",
+ "read": "Đã đọc",
+ "unread": "Chưa đọc",
+ "readedCount": "{count} người đã đọc",
+ "guessWantAsk": "Bạn có thể hỏi",
+ "typing": "Đang nhập...",
+ "autoReplyFailed": "Trả lời tự động thất bại",
+ "receiptMessage": "【Đã đọc】",
+ "inputPlaceholder": "Nhập tin nhắn",
+ "send": "Gửi",
+ "newMessages": "{count} tin nhắn mới",
+ "cannotSendBlank": "Không gửi tin rỗng",
+ "copySuccess": "Sao chép thành công",
+ "copyFailed": "Sao chép thất bại",
+ "downloadFailed": "Tải thất bại",
+ "deleteMessage": "Xóa tin nhắn",
+ "confirmDelete": "Xóa tin này?",
+ "recallMessage": "Thu hồi tin nhắn",
+ "confirmRecall": "Thu hồi tin này?",
+ "deleteSuccess": "Xóa thành công",
+ "resendNotSupported": "Không hỗ trợ gửi lại",
+ "sendFailed": "Gửi thất bại",
+ "uploadFailed": "Tải lên thất bại",
+ "userBanned": "Bạn bị cấm nói: {reason}",
+ "groupBanned": "Bị cấm trong nhóm: {reason}",
+ "atAll": "Tất cả",
+ "file": "Tệp",
+ "album": "Thư viện",
+ "camera": "Máy ảnh",
+ "noFriends": "Chưa có bạn bè",
+ "title": "Trò chuyện"
+ },
+ "common": {
+ "confirm": "Xác nhận",
+ "cancel": "Hủy",
+ "loading": "Đang tải...",
+ "justNow": "Vừa xong",
+ "minutesAgo": "phút trước",
+ "yesterday": "Hôm qua"
+ }
+}
\ No newline at end of file
diff --git a/im-uniapp/static/i18n/zh.json b/im-uniapp/static/i18n/zh.json
new file mode 100644
index 0000000..e73316c
--- /dev/null
+++ b/im-uniapp/static/i18n/zh.json
@@ -0,0 +1,47 @@
+{
+ "chat": {
+ "copy": "复制",
+ "recall": "撤回",
+ "delete": "删除",
+ "download": "下载并打开",
+ "read": "已读",
+ "unread": "未读",
+ "readedCount": "{count}人已读",
+
+ "guessWantAsk": "你可能想问",
+ "typing": "正在输入...",
+ "autoReplyFailed": "自动回复失败",
+ "receiptMessage": "【已读回执】",
+ "inputPlaceholder": "请输入消息",
+ "send": "发送",
+ "newMessages": "{count}条新消息",
+ "cannotSendBlank": "不能发送空消息",
+ "copySuccess": "复制成功",
+ "copyFailed": "复制失败",
+ "downloadFailed": "下载失败",
+ "deleteMessage": "删除消息",
+ "confirmDelete": "确定删除此消息?",
+ "recallMessage": "撤回消息",
+ "confirmRecall": "确定撤回此消息?",
+ "deleteSuccess": "删除成功",
+ "resendNotSupported": "暂不支持重发该类型消息",
+ "sendFailed": "发送失败",
+ "uploadFailed": "上传失败",
+ "userBanned": "你已被禁言,原因:{reason}",
+ "groupBanned": "你已被群禁言,原因:{reason}",
+ "atAll": "所有人",
+ "file": "文件",
+ "album": "相册",
+ "camera": "相机",
+ "noFriends": "暂无好友",
+ "title": "聊天"
+ },
+ "common": {
+ "confirm": "确定",
+ "cancel": "取消",
+ "loading": "加载中...",
+ "justNow": "刚刚",
+ "minutesAgo": "分钟前",
+ "yesterday": "昨天"
+ }
+}
\ No newline at end of file