diff --git a/docs/api-change.md b/docs/api-change.md index f5254955f2..2ffbf3a826 100644 --- a/docs/api-change.md +++ b/docs/api-change.md @@ -2,6 +2,8 @@ Breaking changes are indicated by the :warning: icon. +- Adding `lang` from the response of `i` and the request parameter of `i/update`. + ## v20240504 - :warning: Removed `release` endpoint. diff --git a/docs/downgrade.sql b/docs/downgrade.sql index 787785eb67..6f3901006f 100644 --- a/docs/downgrade.sql +++ b/docs/downgrade.sql @@ -764,9 +764,6 @@ CREATE SEQUENCE public.__chart_day__users_id_seq CACHE 1; ALTER SEQUENCE public.__chart_day__users_id_seq OWNED BY public.__chart_day__users.id; --- drop-user-profile-language -ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32); - -- emoji-moderator ALTER TABLE "user" DROP COLUMN "emojiModPerm"; DROP TYPE "public"."user_emojimodperm_enum"; diff --git a/locales/en-US.yml b/locales/en-US.yml index 08fcc490ea..d5b81b0be1 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -766,6 +766,9 @@ confirmToUnclipAlreadyClippedNote: "This post is already part of the \"{name}\" public: "Public" i18nInfo: "Firefish is being translated into various languages by volunteers. You can help at {link}." +i18nServerInfo: "New clients will be in {language} by default." +i18nServerChange: "Use {language} instead." +i18nServerSet: "Use {language} for new clients." manageAccessTokens: "Manage access tokens" accountInfo: "Account Info" notesCount: "Number of posts" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ef9f9b9bfb..72b7ed2969 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -685,6 +685,9 @@ unclip: "クリップ解除" confirmToUnclipAlreadyClippedNote: "この投稿はすでにクリップ「{name}」に含まれています。投稿をこのクリップから除外しますか?" public: "公開" i18nInfo: "Firefishは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。" +i18nServerInfo: "新しいクライアントはデフォルトで {language} で表示します。" +i18nServerChange: "{language} に変更する。" +i18nServerSet: "新しいクライアントの表示言語も {language} にする。" manageAccessTokens: "アクセストークンの管理" accountInfo: "アカウント情報" notesCount: "投稿の数" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 2b326c4066..9744bb95f7 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -667,6 +667,9 @@ unclip: "移除便签" confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。您想要将本帖从该便签中移除吗?" public: "公开" i18nInfo: "Firefish 已经被志愿者们翻译成了各种语言。如果您也有兴趣,可以通过 {link} 帮助翻译。" +i18nServerInfo: "新客户端将默认使用 {language}。" +i18nServerChange: "改为 {language}。" +i18nServerSet: "设定新客户端使用 {language}。" manageAccessTokens: "管理访问令牌" accountInfo: "账号信息" notesCount: "帖子数量" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 5a722933e6..556a4efb61 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -661,6 +661,9 @@ unclip: "解除摘錄" confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?" public: "公開" i18nInfo: "Firefish已經被志願者們翻譯成各種語言版本,如果想要幫忙的話,可以進入{link}幫助翻譯。" +i18nServerInfo: "新客戶端將默認使用 {language}。" +i18nServerChange: "改爲 {language}。" +i18nServerSet: "設定新客戶端使用 {language}。" manageAccessTokens: "管理存取權杖" accountInfo: "帳戶資訊" notesCount: "貼文數量" diff --git a/packages/backend/src/migration/1714888400293-add-user-profile-language.ts b/packages/backend/src/migration/1714888400293-add-user-profile-language.ts new file mode 100644 index 0000000000..8fb6496402 --- /dev/null +++ b/packages/backend/src/migration/1714888400293-add-user-profile-language.ts @@ -0,0 +1,13 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddUserProfileLanguage1714888400293 implements MigrationInterface { + async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32)`, + ); + } + + async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "lang"`); + } +} diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index 2fe2b7a58a..2b9ac79195 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -50,6 +50,12 @@ export class UserProfile { verified?: boolean; }[]; + @Column("varchar", { + length: 32, + nullable: true, + }) + public lang: string | null; + @Column("varchar", { length: 512, nullable: true, diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index a5a8771645..0f4ea9f72a 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -512,6 +512,7 @@ export const UserRepository = db.getRepository(User).extend({ description: profile!.description, location: profile!.location, birthday: profile!.birthday, + lang: profile!.lang, fields: profile!.fields, followersCount: followersCount ?? null, followingCount: followingCount ?? null, diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts index bcdd718dd1..c7417ce0e4 100644 --- a/packages/backend/src/models/schema/user.ts +++ b/packages/backend/src/models/schema/user.ts @@ -204,6 +204,12 @@ export const packedUserDetailedNotMeOnlySchema = { optional: false, example: "2018-03-12", }, + lang: { + type: "string", + nullable: true, + optional: false, + example: "ja-JP", + }, fields: { type: "array", nullable: false, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 9a2b49cb3d..50f0da8f8d 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -87,6 +87,7 @@ export const paramDef = { description: { ...Users.descriptionSchema, nullable: true }, location: { ...Users.locationSchema, nullable: true }, birthday: { ...Users.birthdaySchema, nullable: true }, + lang: { type: "string", nullable: true }, avatarId: { type: "string", format: "misskey:id", nullable: true }, bannerId: { type: "string", format: "misskey:id", nullable: true }, fields: { @@ -154,6 +155,7 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (ps.name !== undefined) updates.name = ps.name; if (ps.description !== undefined) profileUpdates.description = ps.description; + if (typeof ps.lang === "string") profileUpdates.lang = ps.lang; if (ps.location !== undefined) profileUpdates.location = ps.location; if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; if (ps.ffVisibility !== undefined) diff --git a/packages/client/src/account.ts b/packages/client/src/account.ts index cf6fb54915..fb3f70c34e 100644 --- a/packages/client/src/account.ts +++ b/packages/client/src/account.ts @@ -132,6 +132,9 @@ export async function signIn(token: Account["token"], redirect?: string) { if (_DEV_) console.log("logging as token ", token); const newAccount = await fetchAccount(token); localStorage.setItem("account", JSON.stringify(newAccount)); + if (newAccount.lang) { + localStorage.setItem("lang", newAccount.lang); + } document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う await addAccount(newAccount.id, token); diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index f6c5ffc742..0659e2b1c4 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -14,6 +14,12 @@ > </template> </I18n> + <I18n :src="i18n.ts.i18nServerInfo" v-if="serverLang" tag="span"> + <template #language><strong>{{ langs.find(a => a[0] === serverLang)?.[1] ?? serverLang }}</strong></template> + </I18n> + <button class="_textButton" @click="updateServerLang" v-if="lang && lang !== serverLang"> + {{i18n.t(serverLang ? "i18nServerChange" : "i18nServerSet", { language: langs.find(a => a[0] === lang)?.[1] ?? lang })}} + </button> </template> </FormSelect> @@ -404,6 +410,7 @@ import { deviceKind } from "@/scripts/device-kind"; import icon from "@/scripts/icon"; const lang = ref(localStorage.getItem("lang")); +const serverLang = ref(me?.lang); const translateLang = ref(localStorage.getItem("translateLang")); const fontSize = ref(localStorage.getItem("fontSize")); const useSystemFont = ref(localStorage.getItem("useSystemFont") !== "f"); @@ -559,6 +566,14 @@ const foldNotification = computed( // }); // } +function updateServerLang() { + os.api("i/update", { + lang: lang.value, + }).then((i) => { + serverLang.value = i.lang; + }); +} + watch(swipeOnDesktop, () => { defaultStore.set("swipeOnMobile", true); });