feat: Add language metadata to notes

This commit is contained in:
Essem 2024-02-03 11:45:45 -06:00
parent 2fa0e238b7
commit a76d3cf861
No known key found for this signature in database
GPG key ID: 7D497397CC3A2A8C
20 changed files with 630 additions and 923 deletions

View file

@ -63,6 +63,7 @@ import { trackPromise } from '@/misc/promise-tracker.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isNotNull } from '@/misc/is-not-null.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { langmap } from '@/misc/langmap.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@ -133,6 +134,7 @@ type Option = {
createdAt?: Date | null;
name?: string | null;
text?: string | null;
lang?: string | null;
reply?: MiNote | null;
renote?: MiNote | null;
files?: MiDriveFile[] | null;
@ -337,6 +339,13 @@ export class NoteCreateService implements OnApplicationShutdown {
data.text = null;
}
if (data.lang) {
if (!Object.keys(langmap).includes(data.lang.toLowerCase())) throw new Error('invalid param');
data.lang = data.lang.toLowerCase();
} else {
data.lang = null;
}
let tags = data.apHashtags;
let emojis = data.apEmojis;
let mentionedUsers = data.apMentions;
@ -579,6 +588,7 @@ export class NoteCreateService implements OnApplicationShutdown {
: null,
name: data.name,
text: data.text,
lang: data.lang,
hasPoll: data.poll != null,
cw: data.cw ?? null,
tags: tags.map(tag => normalizeForSearch(tag)),
@ -1004,7 +1014,7 @@ export class NoteCreateService implements OnApplicationShutdown {
removeOnComplete: true,
});
}
// Pack the note
const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true, withReactionAndUserPairCache: true });

View file

@ -52,6 +52,7 @@ import { isReply } from '@/misc/is-reply.js';
import { trackPromise } from '@/misc/promise-tracker.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { langmap } from '@/misc/langmap.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention' | 'edited';
@ -122,6 +123,7 @@ type Option = {
createdAt?: Date | null;
name?: string | null;
text?: string | null;
lang?: string | null;
reply?: MiNote | null;
renote?: MiNote | null;
files?: MiDriveFile[] | null;
@ -358,6 +360,13 @@ export class NoteEditService implements OnApplicationShutdown {
data.text = null;
}
if (data.lang) {
if (!Object.keys(langmap).includes(data.lang.toLowerCase())) throw new Error('invalid param');
data.lang = data.lang.toLowerCase();
} else {
data.lang = null;
}
let tags = data.apHashtags;
let emojis = data.apEmojis;
let mentionedUsers = data.apMentions;
@ -420,6 +429,9 @@ export class NoteEditService implements OnApplicationShutdown {
if (oldnote.hasPoll !== !!data.poll) {
update.hasPoll = !!data.poll;
}
if (data.lang !== oldnote.lang) {
update.lang = data.lang;
}
const poll = await this.pollsRepository.findOneBy({ noteId: oldnote.id });
@ -434,6 +446,7 @@ export class NoteEditService implements OnApplicationShutdown {
oldText: oldnote.text || undefined,
newText: update.text || undefined,
cw: update.cw || undefined,
lang: update.lang || undefined,
fileIds: undefined,
oldDate: exists ? oldnote.updatedAt as Date : this.idService.parse(oldnote.id).date,
updatedAt: new Date(),
@ -453,6 +466,7 @@ export class NoteEditService implements OnApplicationShutdown {
: null,
name: data.name,
text: data.text,
lang: data.lang,
hasPoll: data.poll != null,
cw: data.cw ?? null,
tags: tags.map(tag => normalizeForSearch(tag)),

View file

@ -285,7 +285,7 @@ export class ApRendererService {
if (instance && instance.softwareName === 'pleroma') isMastodon = true;
}
}
const object: ILike = {
type: 'Like',
id: `${this.config.url}/likes/${noteReaction.id}`,
@ -454,6 +454,9 @@ export class ApRendererService {
mediaType: 'text/x.misskeymarkdown',
},
}),
contentMap: note.lang ? {
[note.lang]: content,
} : null,
_misskey_quote: quote,
quoteUrl: quote,
quoteUri: quote,
@ -746,6 +749,9 @@ export class ApRendererService {
mediaType: 'text/x.misskeymarkdown',
},
}),
contentMap: note.lang ? {
[note.lang]: content,
} : null,
_misskey_quote: quote,
quoteUrl: quote,
quoteUri: quote,

View file

@ -25,6 +25,7 @@ import { StatusError } from '@/misc/status-error.js';
import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
import { checkHttps } from '@/misc/check-https.js';
import { langmap } from '@/misc/langmap.js';
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
import { ApLoggerService } from '../ApLoggerService.js';
import { ApMfmService } from '../ApMfmService.js';
@ -244,12 +245,21 @@ export class ApNoteService {
let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
text = note.source.content;
} else if (note.contentMap != null) {
const entry = Object.entries(note.contentMap)[0];
text = this.apMfmService.htmlToMfm(entry[1], note.tag);
} else if (typeof note._misskey_content !== 'undefined') {
text = note._misskey_content;
} else if (typeof note.content === 'string') {
text = this.apMfmService.htmlToMfm(note.content, note.tag);
}
let lang: string | null = null;
if (note.contentMap != null) {
const key = Object.keys(note.contentMap)[0].toLowerCase();
lang = Object.keys(langmap).includes(key) ? key : null;
}
// vote
if (reply && reply.hasPoll) {
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
@ -290,6 +300,7 @@ export class ApNoteService {
name: note.name,
cw,
text,
lang,
localOnly: false,
visibility,
visibleUsers,
@ -452,12 +463,21 @@ export class ApNoteService {
let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
text = note.source.content;
} else if (note.contentMap != null) {
const entry = Object.entries(note.contentMap)[0];
text = this.apMfmService.htmlToMfm(entry[1], note.tag);
} else if (typeof note._misskey_content !== 'undefined') {
text = note._misskey_content;
} else if (typeof note.content === 'string') {
text = this.apMfmService.htmlToMfm(note.content, note.tag);
}
let lang: string | null = null;
if (note.contentMap != null) {
const key = Object.keys(note.contentMap)[0].toLowerCase();
lang = Object.keys(langmap).includes(key) ? key : null;
}
// vote
if (reply && reply.hasPoll) {
const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id });
@ -498,6 +518,7 @@ export class ApNoteService {
name: note.name,
cw,
text,
lang,
localOnly: false,
visibility,
visibleUsers,

View file

@ -21,6 +21,7 @@ export interface IObject {
inReplyTo?: any;
replies?: ICollection;
content?: string | null;
contentMap?: Obj | null;
startTime?: Date;
endTime?: Date;
icon?: any;

View file

@ -17,12 +17,12 @@ import { bindThis } from '@/decorators.js';
import { isNotNull } from '@/misc/is-not-null.js';
import { DebounceLoader } from '@/misc/loader.js';
import { IdService } from '@/core/IdService.js';
import type { Config } from '@/config.js';
import type { OnModuleInit } from '@nestjs/common';
import type { CustomEmojiService } from '../CustomEmojiService.js';
import type { ReactionService } from '../ReactionService.js';
import type { UserEntityService } from './UserEntityService.js';
import type { DriveFileEntityService } from './DriveFileEntityService.js';
import type { Config } from '@/config.js';
@Injectable()
export class NoteEntityService implements OnModuleInit {
@ -120,7 +120,7 @@ export class NoteEntityService implements OnModuleInit {
followerId: meId,
},
});
hide = !isFollowing;
} else {
// フォロワーかどうか
@ -373,6 +373,7 @@ export class NoteEntityService implements OnModuleInit {
uri: note.uri ?? undefined,
url: note.url ?? undefined,
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
lang: note.lang,
...(meId && Object.keys(note.reactions).length > 0 ? {
myReaction: this.populateMyReaction(note, meId, options?._hint_),
} : {}),

View file

@ -4,668 +4,384 @@
*/
// TODO: sharedに置いてフロントエンドのと統合したい
export const langmap = {
'ach': {
nativeName: 'Lwo',
},
'ady': {
nativeName: 'Адыгэбзэ',
},
'af': {
export const iso639Langs1 = {
af: {
nativeName: 'Afrikaans',
},
'af-NA': {
nativeName: 'Afrikaans (Namibia)',
},
'af-ZA': {
nativeName: 'Afrikaans (South Africa)',
},
'ak': {
ak: {
nativeName: 'Tɕɥi',
},
'ar': {
ar: {
nativeName: 'العربية',
rtl: true,
},
'ar-AR': {
nativeName: 'العربية',
},
'ar-MA': {
nativeName: 'العربية',
},
'ar-SA': {
nativeName: 'العربية (السعودية)',
},
'ay-BO': {
ay: {
nativeName: 'Aymar aru',
},
'az': {
az: {
nativeName: 'Azərbaycan dili',
},
'az-AZ': {
nativeName: 'Azərbaycan dili',
},
'be-BY': {
be: {
nativeName: 'Беларуская',
},
'bg': {
bg: {
nativeName: 'Български',
},
'bg-BG': {
nativeName: 'Български',
},
'bn': {
bn: {
nativeName: 'বাংলা',
},
'bn-IN': {
nativeName: 'বাংলা (ভারত)',
},
'bn-BD': {
nativeName: 'বাংলা(বাংলাদেশ)',
},
'br': {
br: {
nativeName: 'Brezhoneg',
},
'bs-BA': {
bs: {
nativeName: 'Bosanski',
},
'ca': {
ca: {
nativeName: 'Català',
},
'ca-ES': {
nativeName: 'Català',
},
'cak': {
nativeName: 'Maya Kaqchikel',
},
'ck-US': {
nativeName: 'ᏣᎳᎩ (tsalagi)',
},
'cs': {
cs: {
nativeName: 'Čeština',
},
'cs-CZ': {
nativeName: 'Čeština',
},
'cy': {
cy: {
nativeName: 'Cymraeg',
},
'cy-GB': {
nativeName: 'Cymraeg',
},
'da': {
da: {
nativeName: 'Dansk',
},
'da-DK': {
nativeName: 'Dansk',
},
'de': {
de: {
nativeName: 'Deutsch',
},
'de-AT': {
nativeName: 'Deutsch (Österreich)',
},
'de-DE': {
nativeName: 'Deutsch (Deutschland)',
},
'de-CH': {
nativeName: 'Deutsch (Schweiz)',
},
'dsb': {
nativeName: 'Dolnoserbšćina',
},
'el': {
el: {
nativeName: 'Ελληνικά',
},
'el-GR': {
nativeName: 'Ελληνικά',
},
'en': {
en: {
nativeName: 'English',
},
'en-GB': {
nativeName: 'English (UK)',
},
'en-AU': {
nativeName: 'English (Australia)',
},
'en-CA': {
nativeName: 'English (Canada)',
},
'en-IE': {
nativeName: 'English (Ireland)',
},
'en-IN': {
nativeName: 'English (India)',
},
'en-PI': {
nativeName: 'English (Pirate)',
},
'en-SG': {
nativeName: 'English (Singapore)',
},
'en-UD': {
nativeName: 'English (Upside Down)',
},
'en-US': {
nativeName: 'English (US)',
},
'en-ZA': {
nativeName: 'English (South Africa)',
},
'en@pirate': {
nativeName: 'English (Pirate)',
},
'eo': {
eo: {
nativeName: 'Esperanto',
},
'eo-EO': {
nativeName: 'Esperanto',
},
'es': {
es: {
nativeName: 'Español',
},
'es-AR': {
nativeName: 'Español (Argentine)',
},
'es-419': {
nativeName: 'Español (Latinoamérica)',
},
'es-CL': {
nativeName: 'Español (Chile)',
},
'es-CO': {
nativeName: 'Español (Colombia)',
},
'es-EC': {
nativeName: 'Español (Ecuador)',
},
'es-ES': {
nativeName: 'Español (España)',
},
'es-LA': {
nativeName: 'Español (Latinoamérica)',
},
'es-NI': {
nativeName: 'Español (Nicaragua)',
},
'es-MX': {
nativeName: 'Español (México)',
},
'es-US': {
nativeName: 'Español (Estados Unidos)',
},
'es-VE': {
nativeName: 'Español (Venezuela)',
},
'et': {
et: {
nativeName: 'eesti keel',
},
'et-EE': {
nativeName: 'Eesti (Estonia)',
},
'eu': {
eu: {
nativeName: 'Euskara',
},
'eu-ES': {
nativeName: 'Euskara',
},
'fa': {
fa: {
nativeName: 'فارسی',
rtl: true,
},
'fa-IR': {
nativeName: 'فارسی',
},
'fb-LT': {
nativeName: 'Leet Speak',
},
'ff': {
ff: {
nativeName: 'Fulah',
},
'fi': {
fi: {
nativeName: 'Suomi',
},
'fi-FI': {
nativeName: 'Suomi',
},
'fo': {
fo: {
nativeName: 'Føroyskt',
},
'fo-FO': {
nativeName: 'Føroyskt (Færeyjar)',
},
'fr': {
fr: {
nativeName: 'Français',
},
'fr-CA': {
nativeName: 'Français (Canada)',
},
'fr-FR': {
nativeName: 'Français (France)',
},
'fr-BE': {
nativeName: 'Français (Belgique)',
},
'fr-CH': {
nativeName: 'Français (Suisse)',
},
'fy-NL': {
fy: {
nativeName: 'Frysk',
},
'ga': {
ga: {
nativeName: 'Gaeilge',
},
'ga-IE': {
nativeName: 'Gaeilge',
},
'gd': {
gd: {
nativeName: 'Gàidhlig',
},
'gl': {
gl: {
nativeName: 'Galego',
},
'gl-ES': {
nativeName: 'Galego',
},
'gn-PY': {
gn: {
nativeName: 'Avañe\'ẽ',
},
'gu-IN': {
gu: {
nativeName: 'ગુજરાતી',
},
'gv': {
gv: {
nativeName: 'Gaelg',
},
'gx-GR': {
nativeName: 'Ἑλληνική ἀρχαία',
},
'he': {
he: {
nativeName: 'עברית‏',
rtl: true,
},
'he-IL': {
nativeName: 'עברית‏',
},
'hi': {
hi: {
nativeName: 'हिन्दी',
},
'hi-IN': {
nativeName: 'हिन्दी',
},
'hr': {
hr: {
nativeName: 'Hrvatski',
},
'hr-HR': {
nativeName: 'Hrvatski',
},
'hsb': {
nativeName: 'Hornjoserbšćina',
},
'ht': {
ht: {
nativeName: 'Kreyòl',
},
'hu': {
hu: {
nativeName: 'Magyar',
},
'hu-HU': {
nativeName: 'Magyar',
},
'hy': {
hy: {
nativeName: 'Հայերեն',
},
'hy-AM': {
nativeName: 'Հայերեն (Հայաստան)',
},
'id': {
id: {
nativeName: 'Bahasa Indonesia',
},
'id-ID': {
nativeName: 'Bahasa Indonesia',
},
'is': {
is: {
nativeName: 'Íslenska',
},
'is-IS': {
nativeName: 'Íslenska (Iceland)',
},
'it': {
it: {
nativeName: 'Italiano',
},
'it-IT': {
nativeName: 'Italiano',
},
'ja': {
ja: {
nativeName: '日本語',
},
'ja-JP': {
nativeName: '日本語 (日本)',
},
'jv-ID': {
jv: {
nativeName: 'Basa Jawa',
},
'ka-GE': {
ka: {
nativeName: 'ქართული',
},
'kk-KZ': {
kk: {
nativeName: 'Қазақша',
},
'km': {
nativeName: 'ភាសាខ្មែរ',
},
'kl': {
kl: {
nativeName: 'kalaallisut',
},
'km-KH': {
km: {
nativeName: 'ភាសាខ្មែរ',
},
'kab': {
nativeName: 'Taqbaylit',
},
'kn': {
kn: {
nativeName: 'ಕನ್ನಡ',
},
'kn-IN': {
nativeName: 'ಕನ್ನಡ (India)',
},
'ko': {
ko: {
nativeName: '한국어',
},
'ko-KR': {
nativeName: '한국어 (한국)',
},
'ku-TR': {
ku: {
nativeName: 'Kurdî',
},
'kw': {
kw: {
nativeName: 'Kernewek',
},
'la': {
la: {
nativeName: 'Latin',
},
'la-VA': {
nativeName: 'Latin',
},
'lb': {
lb: {
nativeName: 'Lëtzebuergesch',
},
'li-NL': {
li: {
nativeName: 'Lèmbörgs',
},
'lt': {
lt: {
nativeName: 'Lietuvių',
},
'lt-LT': {
nativeName: 'Lietuvių',
},
'lv': {
lv: {
nativeName: 'Latviešu',
},
'lv-LV': {
nativeName: 'Latviešu',
},
'mai': {
nativeName: 'मैथिली, মৈথিলী',
},
'mg-MG': {
mg: {
nativeName: 'Malagasy',
},
'mk': {
mk: {
nativeName: 'Македонски',
},
'mk-MK': {
nativeName: 'Македонски (Македонски)',
},
'ml': {
ml: {
nativeName: 'മലയാളം',
},
'ml-IN': {
nativeName: 'മലയാളം',
},
'mn-MN': {
mn: {
nativeName: 'Монгол',
},
'mr': {
mr: {
nativeName: 'मराठी',
},
'mr-IN': {
nativeName: 'मराठी',
},
'ms': {
ms: {
nativeName: 'Bahasa Melayu',
},
'ms-MY': {
nativeName: 'Bahasa Melayu',
},
'mt': {
mt: {
nativeName: 'Malti',
},
'mt-MT': {
nativeName: 'Malti',
},
'my': {
my: {
nativeName: 'ဗမာစကာ',
},
'no': {
no: {
nativeName: 'Norsk',
},
'nb': {
nb: {
nativeName: 'Norsk (bokmål)',
},
'nb-NO': {
nativeName: 'Norsk (bokmål)',
},
'ne': {
ne: {
nativeName: 'नेपाली',
},
'ne-NP': {
nativeName: 'नेपाली',
},
'nl': {
nl: {
nativeName: 'Nederlands',
},
'nl-BE': {
nativeName: 'Nederlands (België)',
},
'nl-NL': {
nativeName: 'Nederlands (Nederland)',
},
'nn-NO': {
nn: {
nativeName: 'Norsk (nynorsk)',
},
'oc': {
oc: {
nativeName: 'Occitan',
},
'or-IN': {
or: {
nativeName: 'ଓଡ଼ିଆ',
},
'pa': {
pa: {
nativeName: 'ਪੰਜਾਬੀ',
},
'pa-IN': {
nativeName: 'ਪੰਜਾਬੀ (ਭਾਰਤ ਨੂੰ)',
},
'pl': {
pl: {
nativeName: 'Polski',
},
'pl-PL': {
nativeName: 'Polski',
},
'ps-AF': {
ps: {
nativeName: 'پښتو',
rtl: true,
},
'pt': {
pt: {
nativeName: 'Português',
},
'pt-BR': {
nativeName: 'Português (Brasil)',
},
'pt-PT': {
nativeName: 'Português (Portugal)',
},
'qu-PE': {
qu: {
nativeName: 'Qhichwa',
},
'rm-CH': {
rm: {
nativeName: 'Rumantsch',
},
'ro': {
ro: {
nativeName: 'Română',
},
'ro-RO': {
nativeName: 'Română',
},
'ru': {
ru: {
nativeName: 'Русский',
},
'ru-RU': {
nativeName: 'Русский',
},
'sa-IN': {
sa: {
nativeName: 'संस्कृतम्',
},
'se-NO': {
se: {
nativeName: 'Davvisámegiella',
},
'sh': {
sh: {
nativeName: 'српскохрватски',
},
'si-LK': {
si: {
nativeName: 'සිංහල',
},
'sk': {
sk: {
nativeName: 'Slovenčina',
},
'sk-SK': {
nativeName: 'Slovenčina (Slovakia)',
},
'sl': {
sl: {
nativeName: 'Slovenščina',
},
'sl-SI': {
nativeName: 'Slovenščina',
},
'so-SO': {
so: {
nativeName: 'Soomaaliga',
},
'sq': {
sq: {
nativeName: 'Shqip',
},
'sq-AL': {
nativeName: 'Shqip',
},
'sr': {
sr: {
nativeName: 'Српски',
},
'sr-RS': {
nativeName: 'Српски (Serbia)',
},
'su': {
su: {
nativeName: 'Basa Sunda',
},
'sv': {
sv: {
nativeName: 'Svenska',
},
'sv-SE': {
nativeName: 'Svenska',
},
'sw': {
sw: {
nativeName: 'Kiswahili',
},
'sw-KE': {
nativeName: 'Kiswahili',
},
'ta': {
ta: {
nativeName: 'தமிழ்',
},
'ta-IN': {
nativeName: 'தமிழ்',
},
'te': {
te: {
nativeName: 'తెలుగు',
},
'te-IN': {
nativeName: 'తెలుగు',
},
'tg': {
tg: {
nativeName: 'забо́ни тоҷикӣ́',
},
'tg-TJ': {
nativeName: 'тоҷикӣ',
},
'th': {
th: {
nativeName: 'ภาษาไทย',
},
'th-TH': {
nativeName: 'ภาษาไทย (ประเทศไทย)',
},
'fil': {
nativeName: 'Filipino',
},
'tlh': {
nativeName: 'tlhIngan-Hol',
},
'tr': {
tr: {
nativeName: 'Türkçe',
},
'tr-TR': {
nativeName: 'Türkçe',
},
'tt-RU': {
tt: {
nativeName: 'татарча',
},
'uk': {
uk: {
nativeName: 'Українська',
},
'uk-UA': {
nativeName: 'Українська',
},
'ur': {
ur: {
nativeName: 'اردو',
rtl: true,
},
'ur-PK': {
nativeName: 'اردو',
},
'uz': {
uz: {
nativeName: 'O\'zbek',
},
'uz-UZ': {
nativeName: 'O\'zbek',
},
'vi': {
vi: {
nativeName: 'Tiếng Việt',
},
'vi-VN': {
nativeName: 'Tiếng Việt',
},
'xh-ZA': {
xh: {
nativeName: 'isiXhosa',
},
'yi': {
yi: {
nativeName: 'ייִדיש',
rtl: true,
},
'yi-DE': {
nativeName: 'ייִדיש (German)',
},
'zh': {
zh: {
nativeName: '中文',
},
'zh-Hans': {
nativeName: '中文简体',
},
'zh-Hant': {
nativeName: '中文繁體',
},
'zh-CN': {
nativeName: '中文(中国大陆)',
},
'zh-HK': {
nativeName: '中文(香港)',
},
'zh-SG': {
nativeName: '中文(新加坡)',
},
'zh-TW': {
nativeName: '中文(台灣)',
},
'zu-ZA': {
zu: {
nativeName: 'isiZulu',
},
};
export const iso639Langs3 = {
ach: {
nativeName: 'Lwo',
},
ady: {
nativeName: 'Адыгэбзэ',
},
cak: {
nativeName: 'Maya Kaqchikel',
},
chr: {
nativeName: 'ᏣᎳᎩ (tsalagi)',
},
dsb: {
nativeName: 'Dolnoserbšćina',
},
fil: {
nativeName: 'Filipino',
},
hsb: {
nativeName: 'Hornjoserbšćina',
},
kab: {
nativeName: 'Taqbaylit',
},
mai: {
nativeName: 'मैथिली, মৈথিলী',
},
tlh: {
nativeName: 'tlhIngan-Hol',
},
tok: {
nativeName: 'Toki Pona',
},
yue: {
nativeName: '粵語',
},
nan: {
nativeName: '閩南語',
},
};
export const langmapNoRegion = Object.assign({}, iso639Langs1, iso639Langs3);
export const iso639Regional = {
'zh-hans': {
nativeName: '中文(简体)',
},
'zh-hant': {
nativeName: '中文(繁體)',
},
};
export const langmap = Object.assign({}, langmapNoRegion, iso639Regional);

View file

@ -61,6 +61,12 @@ export class MiNote {
})
public text: string | null;
@Column('varchar', {
length: 10,
nullable: true,
})
public lang: string | null;
@Column('varchar', {
length: 256, nullable: true,
})

View file

@ -1,4 +1,4 @@
import { Entity, JoinColumn, Column, ManyToOne, PrimaryColumn, Index } from "typeorm";
import { Entity, JoinColumn, Column, ManyToOne, PrimaryColumn, Index } from 'typeorm';
import { id } from './util/id.js';
import { MiNote } from './Note.js';
import type { MiDriveFile } from './DriveFile.js';
@ -11,46 +11,52 @@ export class NoteEdit {
@Index()
@Column({
...id(),
comment: "The ID of note.",
comment: 'The ID of note.',
})
public noteId: MiNote["id"];
public noteId: MiNote['id'];
@ManyToOne((type) => MiNote, {
onDelete: "CASCADE",
onDelete: 'CASCADE',
})
@JoinColumn()
public note: MiNote | null;
@Column("text", {
@Column('text', {
nullable: true,
})
public oldText: string | null;
@Column("text", {
@Column('text', {
nullable: true,
})
public newText: string | null;
@Column("varchar", {
@Column('varchar', {
length: 512,
nullable: true,
})
public cw: string | null;
@Column('varchar', {
length: 10,
nullable: true,
})
public lang: string | null;
@Column({
...id(),
array: true,
default: "{}",
default: '{}',
})
public fileIds: MiDriveFile["id"][];
public fileIds: MiDriveFile['id'][];
@Column("timestamp with time zone", {
comment: "The updated date of the Note.",
@Column('timestamp with time zone', {
comment: 'The updated date of the Note.',
})
public updatedAt: Date;
@Column("timestamp with time zone", {
comment: "The old date from before the edit",
@Column('timestamp with time zone', {
comment: 'The old date from before the edit',
})
public oldDate: Date;
}

View file

@ -2,6 +2,7 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { langmap } from '@/misc/langmap.js';
export const packedNoteSchema = {
type: 'object',
@ -26,6 +27,11 @@ export const packedNoteSchema = {
type: 'string',
optional: false, nullable: true,
},
lang: {
type: 'string',
enum: [...Object.keys(langmap)],
nullable: true,
},
cw: {
type: 'string',
optional: true, nullable: true,

View file

@ -20,6 +20,7 @@ import { isPureRenote } from '@/misc/is-pure-renote.js';
import { MetaService } from '@/core/MetaService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { langmap } from '@/misc/langmap.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -136,6 +137,7 @@ export const paramDef = {
visibleUserIds: { type: 'array', uniqueItems: true, items: {
type: 'string', format: 'misskey:id',
} },
lang: { type: 'string', enum: Object.keys(langmap), nullable: true, maxLength: 10 },
cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
localOnly: { type: 'boolean', default: false },
reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
@ -370,6 +372,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
} : undefined,
text: ps.text ?? undefined,
lang: ps.lang,
reply,
renote,
cw: ps.cw,

View file

@ -12,6 +12,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { NoteEditService } from '@/core/NoteEditService.js';
import { DI } from '@/di-symbols.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { langmap } from '@/misc/langmap.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -164,6 +165,7 @@ export const paramDef = {
format: 'misskey:id',
},
},
lang: { type: 'string', enum: Object.keys(langmap), nullable: true, maxLength: 10 },
cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
localOnly: { type: 'boolean', default: false },
reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
@ -301,7 +303,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.renoteId === ps.editId) {
throw new ApiError(meta.errors.cannotQuoteCurrentPost);
}
if (ps.renoteId != null) {
// Fetch renote to note
renote = await this.notesRepository.findOneBy({ id: ps.renoteId });
@ -378,6 +380,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
}
if (ps.lang) {
if (!Object.keys(langmap).includes(ps.lang.toLowerCase())) throw new Error('invalid param');
ps.lang = ps.lang.toLowerCase();
} else {
ps.lang = null;
}
let channel: MiChannel | null = null;
if (ps.channelId != null) {
channel = await this.channelsRepository.findOneBy({ id: ps.channelId, isArchived: false });
@ -396,6 +405,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
} : undefined,
text: ps.text ?? undefined,
lang: ps.lang,
reply,
renote,
cw: ps.cw,