このコミットが含まれているのは:
2026-10-18 00:20:30 +09:00
コミット 9be0c7b1d9
45個のファイルの変更、355行の追加、199行の削除
+6 -1
ファイルの表示
@@ -8,6 +8,8 @@ const COLUMN_COUNT = 12
const DevModeWatermark: FC = () => {
const { t } = useTranslation ('shared')
const text = t ('labels.development', { returnDetails: true })
return (
<div
aria-hidden="true"
@@ -29,13 +31,14 @@ const DevModeWatermark: FC = () => {
{Array.from ({ length: COLUMN_COUNT }, (_, columnIndex) => (
<span
key={columnIndex}
lang={text.usedLng}
className={
'whitespace-nowrap text-3xl font-bold '
+ 'tracking-[0.3em] text-neutral-950/5 '
+ 'dark:text-white/10'
}
style={{ fontFamily: 'Nikumaru' }}>
開発モード
{text.res}
</span>))}
</div>))}
</div>
@@ -44,3 +47,5 @@ const DevModeWatermark: FC = () => {
export default DevModeWatermark
import { useTranslation } from 'react-i18next'
+9 -9
ファイルの表示
@@ -4,6 +4,7 @@ import nikumaru from '@/assets/fonts/nikumaru.otf'
import errorImg from '@/assets/images/not-found.gif'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -11,17 +12,16 @@ type Props = { status: number }
const ErrorScreen: FC<Props> = ({ status }) => {
const [message, rightMsg, leftMsg]: [string, string, string] = (() => {
const { message: translate, label } = useUITranslation ('errors')
const [message]: [string, string, string] = (() => {
switch (status)
{
case 403:
return ['権限ないよ(笑)', '帰れ!', '帰れ!']
case 404:
return ['ページないよ(笑)', '帰れ!', '帰れ!']
case 500:
return ['鯖でエラー出たって(嘲笑)', '管理人に', '聯絡を!']
case 503:
return ['鯖死んでるよ(泣)', '管理人に', '聯絡を!']
return [translate (`${ status }.message`).text,
translate (`${ status }.right`).text, translate (`${ status }.left`).text]
default:
throw new Error
}
@@ -51,11 +51,11 @@ const ErrorScreen: FC<Props> = ({ status }) => {
style={{ fontFamily: 'Nikumaru' }}>
<p>{status}</p>
<div className="flex flex-row space-x-1 sm:space-x-2 md:space-x-4">
<p style={{ writingMode: 'vertical-rl' }}>{leftMsg}</p>
<img className="max-w-[70vw]" src={errorImg} alt="逃げたギター"/>
<p style={{ writingMode: 'vertical-rl' }}>{rightMsg}</p>
<p style={{ writingMode: 'vertical-rl' }}>{label (`${ status }.left`)}</p>
<img className="max-w-[70vw]" src={errorImg} alt={translate ('imageAlt').text}/>
<p style={{ writingMode: 'vertical-rl' }}>{label (`${ status }.right`)}</p>
</div>
<p className="mr-[-.5em]">{message}</p>
<p className="mr-[-.5em]">{label (`${ status }.message`)}</p>
</div>
</MainArea>)
}
+3 -1
ファイルの表示
@@ -3,6 +3,7 @@
import { useRef, useState } from 'react'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import TagSearchBox from '@/components/TagSearchBox'
import FormField from '@/components/common/FormField'
import TextArea from '@/components/common/TextArea'
@@ -39,6 +40,7 @@ type Props = Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'onChange' | '
const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
const { label } = useUITranslation ('navigation')
const locale = useLocale ()
const ref = useRef<HTMLTextAreaElement> (null)
@@ -79,7 +81,7 @@ const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
}
return (
<FormField className="relative w-full" label="タグ" messages={errors}>
<FormField className="relative w-full" label={label ('tags')} messages={errors}>
{({ describedBy, invalid }) => (
<>
<TextArea
+3 -1
ファイルの表示
@@ -3,6 +3,7 @@ import { useRef } from 'react'
import { useLocation } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
import { useUITranslation } from '@/lib/useUITranslation'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { cn } from '@/lib/utils'
import { useSharedTransitionStore } from '@/stores/sharedTransitionStore'
@@ -16,6 +17,7 @@ type Props = { posts: Post[]
const PostList: FC<Props> = ({ posts, onClick }) => {
const { label } = useUITranslation ('shared')
const location = useLocation ()
const behaviourSettings = useClientBehaviourSettings ()
const thumbnailMode = behaviourSettings.thumbnailMode ?? 'normal'
@@ -75,7 +77,7 @@ const PostList: FC<Props> = ({ posts, onClick }) => {
? (
<div className="flex h-full w-full items-center justify-center
bg-muted text-center text-xs text-muted-foreground">
サムネイルなし
{label ('labels.noThumbnail')}
</div>)
: (
<img
+9 -6
ファイルの表示
@@ -1,6 +1,7 @@
import DateTimeField, { toMinutePrecisionIsoUtc } from '@/components/common/DateTimeField'
import FormField from '@/components/common/FormField'
import { Button } from '@/components/ui/button'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -19,9 +20,11 @@ const PostOriginalCreatedTimeField: FC<Props> = (
setOriginalCreatedFrom,
originalCreatedBefore,
setOriginalCreatedBefore,
errors }: Props) => {
errors }: Props,
) => {
const { label } = useUITranslation ('shared')
return (
<FormField label="オリジナルの作成日時" messages={errors}>
<FormField label={label ('labels.originalCreatedAt')} messages={errors}>
{({ describedBy, invalid }) => (
<>
<div className="my-1 flex">
@@ -46,7 +49,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
d.setMinutes (d.getMinutes () + 1)
setOriginalCreatedBefore (toMinutePrecisionIsoUtc (d.toISOString ()))
}}/>
以降
{label ('labels.since')}
</div>
<div>
<Button
@@ -56,7 +59,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
onClick={() => {
setOriginalCreatedFrom (null)
}}>
リセット
{label ('actions.reset')}
</Button>
</div>
</div>
@@ -71,7 +74,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
invalid={invalid}
value={originalCreatedBefore ?? undefined}
onChange={setOriginalCreatedBefore}/>
より前
{label ('labels.before')}
</div>
<div>
<Button
@@ -81,7 +84,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
onClick={() => {
setOriginalCreatedBefore (null)
}}>
リセット
{label ('actions.reset')}
</Button>
</div>
</div>
+3 -1
ファイルの表示
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { apiGet } from '@/lib/api'
import { inputClass } from '@/lib/utils'
@@ -15,6 +16,7 @@ import type { Tag } from '@/types'
const TagSearch: FC = () => {
const { message } = useUITranslation ('shared')
const locale = useLocale ()
const location = useLocation ()
const navigate = useLocaleNavigate ()
@@ -108,7 +110,7 @@ const TagSearch: FC = () => {
<div className="relative w-full">
<input type="text"
data-shortcut-focus-search="true"
placeholder="タグ検索..."
placeholder={message ('labels.tagSearch').text}
value={search}
onChange={whenChanged}
onFocus={() => setSuggestionsVsbl (true)}
+6 -4
ファイルの表示
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import TagLink from '@/components/TagLink'
import TagSearch from '@/components/TagSearch'
import SectionTitle from '@/components/common/SectionTitle'
@@ -23,6 +24,7 @@ type Props = { posts: Post[]
const TagSidebar: FC<Props> = ({ posts, onClick }) => {
const { label } = useUITranslation ('shared')
const locale = useLocale ()
const navigate = useLocaleNavigate ()
const behaviourSettings = useClientBehaviourSettings ()
@@ -71,7 +73,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
const TagBlock = (
<>
<SectionTitle>タグ</SectionTitle>
<SectionTitle>{label ('navigation:tags')}</SectionTitle>
<ul>
{CATEGORIES.flatMap (cat => cat in tags ? (
tags[cat].map (tag => (
@@ -90,7 +92,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
</motion.div>)}
</li>))) : [])}
</ul>
<SectionTitle>関聯</SectionTitle>
<SectionTitle>{label ('labels.relations')}</SectionTitle>
<a href="#"
onClick={ev => {
ev.preventDefault ()
@@ -108,7 +110,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
}
}) ())
}}>
ランダム
{label ('actions.random')}
</a>
</>)
@@ -148,7 +150,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
ev.preventDefault ()
setTagsVsbl (v => !(v))
}}>
{tagsVsbl ? '▲▲▲ タグ一覧を閉じる ▲▲▲' : '▼▼▼ タグ一覧を表示 ▼▼▼'}
{tagsVsbl ? label ('labels.hideTags') : label ('labels.showTags')}
</a>
</SidebarComponent>)
}
+12 -11
ファイルの表示
@@ -16,6 +16,7 @@ import { isLocaleCode,
switchLocalePath,
unlocalisedPath } from '@/lib/locales'
import { uiMessage, uiMessageLanguage } from '@/lib/messages'
import { useUITranslation } from '@/lib/useUITranslation'
import { materialsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { canEditContent } from '@/lib/users'
@@ -125,6 +126,7 @@ export const menuOutline = (
const TopNav: FC<Props> = ({ user, locales }) => {
const { label } = useUITranslation ('shared')
const locale = useLocale ()
const localeName = locales.find (entry => entry.code === locale)?.name
const navigate = useLocaleNavigate ()
@@ -206,21 +208,21 @@ const TopNav: FC<Props> = ({ user, locales }) => {
const topNavTransition =
animationsOff
? { duration: 0 }
: reducedAnimations
? { duration: .08, ease: 'linear' as const }
: { duration: .2, ease: 'easeOut' as const }
: (reducedAnimations
? { duration: .08, ease: 'linear' as const }
: { duration: .2, ease: 'easeOut' as const })
const opacityTransition =
animationsOff
? { duration: 0 }
: reducedAnimations
? { duration: .08 }
: { duration: .12 }
: (reducedAnimations
? { duration: .08 }
: { duration: .12 })
const highlightTransitionClass =
animationsOff
? undefined
: reducedAnimations
? 'transition-[transform,width] duration-75 ease-linear'
: 'transition-[transform,width] duration-200 ease-out'
: (reducedAnimations
? 'transition-[transform,width] duration-75 ease-linear'
: 'transition-[transform,width] duration-200 ease-out')
const prevActiveIdxRef = useRef<number> (activeIdx)
@@ -382,8 +384,7 @@ const TopNav: FC<Props> = ({ user, locales }) => {
onClick={() => {
scroll (0, 0)
}}>
{/* TODO: ここも外出ししろ. */}
ぼざクリ タグ広場
{label ('labels.brand')}
</PrefetchLink>
<div ref={navRef}
+3 -1
ファイルの表示
@@ -1,6 +1,7 @@
import Separator from '@/components/MenuSeparator'
import PrefetchLink from '@/components/PrefetchLink'
import { cn } from '@/lib/utils'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -11,6 +12,7 @@ type Props = { user: User | null,
const TopNavUser: FC<Props> = ({ user, sp }) => {
const { label } = useUITranslation ('shared')
if (!(user))
return
@@ -24,7 +26,7 @@ const TopNavUser: FC<Props> = ({ user, sp }) => {
<>
{sp && <Separator/>}
<PrefetchLink to="/users/settings" className={className}>
{user.name || '名もなきニジラー'}
{user.name || label ('labels.anonymous')}
</PrefetchLink>
</>)
}
+4 -1
ファイルの表示
@@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown'
import remarkGFM from 'remark-gfm'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import PrefetchLink from '@/components/PrefetchLink'
import { wikiKeys } from '@/lib/queryKeys'
import remarkWikiAutoLink from '@/lib/remark-wiki-autolink'
@@ -27,6 +28,7 @@ const mdComponents = { a: (({ href, children }) => (
const WikiBody: FC<Props> = ({ title, body }) => {
const { message } = useUITranslation ('wiki')
const locale = useLocale ()
const { data } = useQuery ({
enabled: Boolean (body),
@@ -39,7 +41,8 @@ const WikiBody: FC<Props> = ({ title, body }) => {
return (
<ReactMarkdown components={mdComponents} remarkPlugins={remarkPlugins}>
{body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`}
{body || message ('missingPage', {
url: `/wiki/new?title=${ encodeURIComponent (title) }` }).text}
</ReactMarkdown>)
}
+3 -2
ファイルの表示
@@ -1,6 +1,7 @@
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
import PostTagsField from '@/components/posts/PostTagsField'
import PostTextField from '@/components/posts/PostTextField'
import TranslatedText from '@/components/TranslatedText'
import type { FC, ReactNode } from 'react'
@@ -54,7 +55,7 @@ const PostCoreDataFields: FC<PostCoreDataFieldsProps> = (
) => (
<>
<PostTextField
label="タイトル"
label={<TranslatedText namespace="shared" messageKey="labels.title"/>}
value={title.value}
disabled={title.disabled}
warnings={title.warnings}
@@ -82,7 +83,7 @@ const PostCoreDataFields: FC<PostCoreDataFieldsProps> = (
rows={tags.rows}/>
<PostTextField
label="親投稿"
label={<TranslatedText namespace="shared" messageKey="labels.parentPosts"/>}
value={parentPostIds.value}
disabled={parentPostIds.disabled}
warnings={parentPostIds.warnings}
+3 -3
ファイルの表示
@@ -1,4 +1,5 @@
import PostTextField from '@/components/posts/PostTextField'
import TranslatedText from '@/components/TranslatedText'
import type { FC } from 'react'
@@ -16,12 +17,11 @@ const PostDurationField: FC<Props> = (
disabled },
) => (
<PostTextField
label="動画時間"
label={<TranslatedText namespace="shared" messageKey="labels.duration"/>}
value={value}
onChange={onChange}
errors={errors}
disabled={disabled}
type="text"/>
)
type="text"/>)
export default PostDurationField
+1 -1
ファイルの表示
@@ -5,7 +5,7 @@ import { inputClass } from '@/lib/utils'
import type { FC, ReactNode } from 'react'
type Props = {
label: string
label: ReactNode
value: string
onChange: (value: string) => void
warnings?: string[]
+4 -2
ファイルの表示
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { cn } from '@/lib/utils'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -15,10 +16,11 @@ type Props = {
const PostThumbnailPreview: FC<Props> = (
{ url,
file,
alt = 'サムネール',
alt,
className = 'h-16 w-16',
referrerPolicy },
) => {
const { message } = useUITranslation ('shared')
const [failed, setFailed] = useState (false)
const [fileUrl, setFileUrl] = useState<string | null> (null)
@@ -55,7 +57,7 @@ const PostThumbnailPreview: FC<Props> = (
return (
<img
src={resolvedUrl}
alt={alt}
alt={alt ?? message ('labels.thumbnail').text}
referrerPolicy={referrerPolicy}
className={cn (className, 'rounded border border-border object-cover')}
onError={() => setFailed (true)}/>)
+2 -9
ファイルの表示
@@ -1,4 +1,5 @@
import StatusBadge from '@/components/common/StatusBadge'
import TranslatedText from '@/components/TranslatedText'
import type { FC } from 'react'
@@ -8,14 +9,6 @@ import type { PostImportBadgeValue } from '@/components/posts/import/postImportR
type Props = {
value: PostImportBadgeValue }
const LABELS: Record<PostImportBadgeValue, string> = {
ready: '登録可能',
error: '登録不可',
warning: '警告',
skipped: 'スキップ',
created: '登録済み',
failed: '登録失敗' }
const TONES: Record<PostImportBadgeValue, StatusBadgeTone> = {
ready: 'success',
error: 'warning',
@@ -27,7 +20,7 @@ const TONES: Record<PostImportBadgeValue, StatusBadgeTone> = {
const PostImportStatusBadge: FC<Props> = ({ value }) => (
<StatusBadge tone={TONES[value]}>
{LABELS[value]}
<TranslatedText namespace="imports" messageKey={`status.${ value }`}/>
</StatusBadge>)
export default PostImportStatusBadge
+1 -1
ファイルの表示
@@ -12,7 +12,7 @@ type Props = {
const PostImportThumbnailPreview: FC<Props> = (
{ url,
file,
alt = 'サムネール',
alt,
className = 'h-16 w-16' },
) => (
<PostThumbnailPreview
+18 -15
ファイルの表示
@@ -1,13 +1,6 @@
import { createInstance } from 'i18next'
import { initReactI18next } from 'react-i18next'
import ainuNavigation from '@/locales/ain-Kana/navigation.json'
import ainuCommon from '@/locales/ain-Kana/common.json'
import ainuTags from '@/locales/ain-Kana/tags.json'
import nipponeseNavigation from '@/locales/ja/navigation.json'
import nipponeseCommon from '@/locales/ja/common.json'
import nipponeseTags from '@/locales/ja/tags.json'
import type { LocaleCode } from '@/lib/locales'
export const i18n = createInstance ()
@@ -17,6 +10,8 @@ export const i18n = createInstance ()
const translatedResource = (resource: Record<string, unknown>): Record<string, unknown> => {
const untranslated = new Set (
Array.isArray (resource._untranslated) ? resource._untranslated : [])
if (untranslated.has ('*'))
return { }
const select = (entries: Record<string, unknown>, prefix = ''): Record<string, unknown> =>
Object.fromEntries (Object.entries (entries).flatMap (([key, value]) => {
const path = prefix ? `${ prefix }.${ key }` : key
@@ -32,6 +27,19 @@ const translatedResource = (resource: Record<string, unknown>): Record<string, u
}
const dictionaries = import.meta.glob<Record<string, unknown>> ('../locales/*/*.json', {
eager: true, import: 'default' })
const resources: Record<string, Record<string, Record<string, unknown>>> = { }
for (const [path, dictionary] of Object.entries (dictionaries))
{
const [, , locale, filename] = path.split ('/')
resources[locale] ??= { }
resources[locale][filename.slice (0, -5)] =
locale === 'ain-Kana' ? translatedResource (dictionary) : dictionary
}
const namespaces = Object.keys (resources.ja ?? { })
void i18n.use (initReactI18next).init ({
lng: 'ja',
fallbackLng: 'ja',
@@ -39,13 +47,8 @@ void i18n.use (initReactI18next).init ({
load: 'currentOnly',
initAsync: false,
defaultNS: 'navigation',
ns: ['navigation', 'common', 'tags'],
resources: {
ja: { navigation: nipponeseNavigation, common: nipponeseCommon, tags: nipponeseTags },
'ain-Kana': {
navigation: ainuNavigation,
common: translatedResource (ainuCommon),
tags: translatedResource (ainuTags) } },
ns: namespaces,
resources,
interpolation: { escapeValue: false },
returnNull: false,
returnEmptyString: false,
@@ -77,7 +80,7 @@ export const untranslatedKeys = (
if (import.meta.env.DEV)
{
for (const namespace of ['navigation', 'common', 'tags'])
for (const namespace of namespaces)
{
const missing = untranslatedKeys ('ain-Kana', namespace)
if (missing.length > 0)
+9 -1
ファイルの表示
@@ -1,5 +1,7 @@
import { createElement } from 'react'
import { useTranslation } from 'react-i18next'
import LocalisedText from '@/components/LocalisedText'
import { useLocale } from '@/lib/useLocale'
@@ -12,5 +14,11 @@ export const useUITranslation = (namespace = 'common') => {
return { text: result.res, language: result.usedLng }
}
return { message }
const label = (key: string, values?: Record<string, string | number>) => {
const result = message (key, values)
return createElement (LocalisedText, {
language: result.language, children: result.text })
}
return { message, label }
}
+22 -37
ファイルの表示
@@ -1,38 +1,23 @@
{
"_untranslated": [
"countLabel", "categoryLabel", "stateLabel", "loading", "fetchError",
"states", "categories.deerjikist", "categories.meme", "categories.character",
"categories.general", "categories.meta", "categories.nico", "pagination", "tagLink"
],
"countLabel": "件数",
"categoryLabel": "カテゴリ",
"stateLabel": "状態",
{ "countLabel": "チピㇱキㇷ゚",
"categoryLabel": "イソネカ",
"stateLabel": "シㇼキ",
"search": "イフナラ",
"loading": "Loading...",
"fetchError": "データ取得エラー",
"states": {
"active": "有効",
"deprecated": "廃止"
},
"categories": {
"deerjikist": "ニジラー",
"meme": "原作・ネタ元・ミーム等",
"character": "キャラクター",
"general": "一般",
"material": "チカㇻペ",
"meta": "メタタグ",
"nico": "ニコニコタグ"
},
"pagination": {
"label": "Pagination",
"first": "最初のページ",
"previous": "前のページ",
"next": "次のページ",
"last": "最後のページ"
},
"tagLink": {
"missingMaterial": "{{name}} 素材情報が存在しません.",
"missingDeerjikist": "{{name}} に関する情報が存在しません.",
"missingWiki": "{{name}} Wiki が存在しません."
}
}
"loading": "タツ゚ㇺ アウㇰ コㇿ アン…",
"fetchError": "タツ゚ㇺ アウㇰ エアイカㇷ゚",
"states": { "active": "アㇱカイ",
"deprecated": "タネ ソモ アエイワンケ" },
"categories": { "deerjikist": "ユㇰ ニチカ ウタㇻ",
"meme": "MEME",
"character": "イノカ",
"general": "ウサ オルㇱペ",
"material": "チカㇻペ",
"meta": "タㇰ オルㇱペ タㇰ",
"nico": "ニコニコ タㇰ" },
"pagination": { "label": "ソㇱ",
"first": "イヨッタ ホㇱキ ソㇱ",
"previous": "ホㇱキ ソㇱ",
"next": "トゥタヌ ソㇱ",
"last": "イヨッタ イヨㇱノ アン ソㇱ" },
"tagLink": { "missingMaterial": "{{name}}:チカㇻペ アスㇽソㇱ ソモ アン。",
"missingDeerjikist": "{{name}}:アスㇽソㇱ ソモ アン。",
"missingWiki": "{{name}}:Wiki ソモ アン。" } }
+8
ファイルの表示
@@ -0,0 +1,8 @@
{
"_untranslated": ["*"],
"403": { "message": "権限ないよ(笑)", "right": "帰れ!", "left": "帰れ!" },
"404": { "message": "ページないよ(笑)", "right": "帰れ!", "left": "帰れ!" },
"500": { "message": "鯖でエラー出たって(嘲笑)", "right": "管理人に", "left": "聯絡を!" },
"503": { "message": "鯖死んでるよ(泣)", "right": "管理人に", "left": "聯絡を!" },
"imageAlt": "逃げたギター"
}
+6
ファイルの表示
@@ -0,0 +1,6 @@
{
"_untranslated": ["*"],
"title": "ヘルプ", "search": "ヘルプ検索", "history": "ヘルプ改定履歴",
"effectiveSince": "有効開始年月日", "empty": "該当する記事がありません.",
"diffTitle": "ヘルプ 差分: {{title}}", "articleTitle": "{{title}} ヘルプ"
}
+7
ファイルの表示
@@ -0,0 +1,7 @@
{
"_untranslated": ["*"],
"status": {
"ready": "登録可能", "error": "登録不可", "warning": "警告",
"skipped": "スキップ", "created": "登録済み", "failed": "登録失敗"
}
}
+1 -1
ファイルの表示
@@ -7,7 +7,7 @@
"allHistory": "フㇱコアンペ オピッタ",
"help": "ヤイカオパㇱ",
"tags": "タㇰ",
"master": "タㇰ オピッタ",
"master": "オピッタ",
"nicoLinks": "ニコニコ ウコテㇷ゚",
"materials": "チカㇻペ",
"suppressions": "アエアイカㇷ゚テㇷ゚",
+30
ファイルの表示
@@ -0,0 +1,30 @@
{
"_untranslated": ["*"],
"labels": {
"title": "タイトル", "content": "内容", "body": "本文",
"lastUpdated": "最終更新", "thumbnail": "サムネール",
"noThumbnail": "サムネイルなし", "thumbnailMissing": "サムネールなし",
"parentPosts": "親投稿", "duration": "動画時間", "platform": "プラットフォーム",
"code": "コード", "information": "情報", "relations": "関聯",
"uploadedBy": "耕作者: ", "uploadedAt": "耕作日時: ", "link": "リンク: ",
"originalPostedAt": "オリジナルの投稿日時: ", "originalCreatedAt": "オリジナルの作成日時",
"brand": "ぼざクリ タグ広場", "since": "以降", "before": "より前",
"tagSearch": "タグ検索...", "hideTags": "▲▲▲ タグ一覧を閉じる ▲▲▲",
"showTags": "▼▼▼ タグ一覧を表示 ▼▼▼",
"resultsCount": "{{count}} 件", "menu": "メニュー", "anonymous": "名もなきニジラー",
"bot": "bot操作", "deprecatedSuffix": "(廃止)", "development": "開発モード"
},
"actions": {
"update": "更新", "edit": "編輯", "add": "追加", "reset": "リセット",
"cancel": "取消", "retry": "再試行", "skip": "スキップ", "random": "ランダム",
"old": "< 古", "new": "新 >", "oldest": "(最古)", "latest": "(最新)",
"diff": "差分", "restore": "復元", "show": "表示"
},
"messages": {
"loading": "読み込み中...", "loadingEllipsis": "読込中……",
"updated": "更新しました.", "updateFailed": "更新失敗", "failed": "失敗……",
"checkInput": "入力を確認してください.", "checkInputModern": "入力を確認してください。",
"checkInputContent": "入力内容を確認してください.", "reviewInput": "入力を見直してください.",
"postSuccess": "投稿成功!", "postFailed": "投稿失敗", "resetChanges": "変更をリセットしますか?"
}
}
+3 -7
ファイルの表示
@@ -1,8 +1,5 @@
{
"_untranslated": [
"name", "postCount", "firstRecordedAt", "definitionUpdatedAt", "aliases",
"parents", "firstRecordedColumn", "updatedAt", "empty"
],
{ "_untranslated": ["name", "postCount", "firstRecordedAt", "definitionUpdatedAt", "aliases",
"parents", "firstRecordedColumn", "updatedAt", "empty"],
"title": "タㇰ",
"name": "名前",
"postCount": "広場の投稿数",
@@ -13,5 +10,4 @@
"firstRecordedColumn": "最初の記載日時",
"updatedAt": "更新日時",
"history": "フㇱコアンペ",
"empty": "結果ないよ(笑)"
}
"empty": "結果ないよ(笑)" }
+9
ファイルの表示
@@ -0,0 +1,9 @@
{
"_untranslated": ["*"],
"newPage": "新規 Wiki ページ",
"editPage": "Wiki ページを編輯",
"history": "Wiki 変更履歴",
"historyHeading": "Wiki 履歴", "change": "変更", "dateTime": "日時",
"diffTitle": "Wiki 差分: {{title}}",
"missingPage": "このページは存在しません。[新規作成してください]({{url}})。"
}
+7
ファイルの表示
@@ -0,0 +1,7 @@
{
"403": { "message": "権限ないよ(笑)", "right": "帰れ!", "left": "帰れ!" },
"404": { "message": "ページないよ(笑)", "right": "帰れ!", "left": "帰れ!" },
"500": { "message": "鯖でエラー出たって(嘲笑)", "right": "管理人に", "left": "聯絡を!" },
"503": { "message": "鯖死んでるよ(泣)", "right": "管理人に", "left": "聯絡を!" },
"imageAlt": "逃げたギター"
}
+5
ファイルの表示
@@ -0,0 +1,5 @@
{
"title": "ヘルプ", "search": "ヘルプ検索", "history": "ヘルプ改定履歴",
"effectiveSince": "有効開始年月日", "empty": "該当する記事がありません.",
"diffTitle": "ヘルプ 差分: {{title}}", "articleTitle": "{{title}} ヘルプ"
}
+6
ファイルの表示
@@ -0,0 +1,6 @@
{
"status": {
"ready": "登録可能", "error": "登録不可", "warning": "警告",
"skipped": "スキップ", "created": "登録済み", "failed": "登録失敗"
}
}
+29
ファイルの表示
@@ -0,0 +1,29 @@
{
"labels": {
"title": "タイトル", "content": "内容", "body": "本文",
"lastUpdated": "最終更新", "thumbnail": "サムネール",
"noThumbnail": "サムネイルなし", "thumbnailMissing": "サムネールなし",
"parentPosts": "親投稿", "duration": "動画時間", "platform": "プラットフォーム",
"code": "コード", "information": "情報", "relations": "関聯",
"uploadedBy": "耕作者: ", "uploadedAt": "耕作日時: ", "link": "リンク: ",
"originalPostedAt": "オリジナルの投稿日時: ", "originalCreatedAt": "オリジナルの作成日時",
"brand": "ぼざクリ タグ広場", "since": "以降", "before": "より前",
"tagSearch": "タグ検索...", "hideTags": "▲▲▲ タグ一覧を閉じる ▲▲▲",
"showTags": "▼▼▼ タグ一覧を表示 ▼▼▼",
"resultsCount": "{{count}} 件", "menu": "メニュー", "anonymous": "名もなきニジラー",
"bot": "bot操作", "deprecatedSuffix": "(廃止)", "development": "開発モード"
},
"actions": {
"update": "更新", "edit": "編輯", "add": "追加", "reset": "リセット",
"cancel": "取消", "retry": "再試行", "skip": "スキップ", "random": "ランダム",
"old": "< 古", "new": "新 >", "oldest": "(最古)", "latest": "(最新)",
"diff": "差分", "restore": "復元", "show": "表示"
},
"messages": {
"loading": "読み込み中...", "loadingEllipsis": "読込中……",
"updated": "更新しました.", "updateFailed": "更新失敗", "failed": "失敗……",
"checkInput": "入力を確認してください.", "checkInputModern": "入力を確認してください。",
"checkInputContent": "入力内容を確認してください.", "reviewInput": "入力を見直してください.",
"postSuccess": "投稿成功!", "postFailed": "投稿失敗", "resetChanges": "変更をリセットしますか?"
}
}
+2 -4
ファイルの表示
@@ -1,5 +1,4 @@
{
"title": "タグ",
{ "title": "タグ",
"name": "名前",
"postCount": "広場の投稿数",
"firstRecordedAt": "はじめて記載された日時",
@@ -9,5 +8,4 @@
"firstRecordedColumn": "最初の記載日時",
"updatedAt": "更新日時",
"history": "耕作履歴",
"empty": "結果ないよ(笑)"
}
"empty": "結果ないよ(笑)" }
+8
ファイルの表示
@@ -0,0 +1,8 @@
{
"newPage": "新規 Wiki ページ",
"editPage": "Wiki ページを編輯",
"history": "Wiki 変更履歴",
"historyHeading": "Wiki 履歴", "change": "変更", "dateTime": "日時",
"diffTitle": "Wiki 差分: {{title}}",
"missingPage": "このページは存在しません。[新規作成してください]({{url}})。"
}
+3 -1
ファイルの表示
@@ -6,6 +6,7 @@ import { menuOutline } from '@/components/TopNav'
import SectionTitle from '@/components/common/SectionTitle'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -13,13 +14,14 @@ import type { User } from '@/types'
const MorePage: FC = () => {
const { message } = useUITranslation ('shared')
const menu = menuOutline (
{ tag: null, wikiId: null, user: { } as User, pathName: location.pathname })
return (
<MainArea className="md:flex">
<Helmet>
<title>{`メニュー | ${ SITE_TITLE }`}</title>
<title>{message ('labels.menu').text} | {SITE_TITLE}</title>
</Helmet>
{[...Array (Math.ceil (menu.length / 4)).keys ()].map (i => (
+8 -5
ファイルの表示
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react'
import { useParams } from 'react-router-dom'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import TagLink from '@/components/TagLink'
import FieldError from '@/components/common/FieldError'
import FormField from '@/components/common/FormField'
@@ -25,6 +26,7 @@ type DeerjikistFormField =
const DeerjikistDetailPage: FC = () => {
const { label } = useUITranslation ('shared')
const locale = useLocale ()
const { id } = useParams ()
const tagId = String (id ?? '')
@@ -54,12 +56,13 @@ const DeerjikistDetailPage: FC = () => {
setData (await apiPut<Deerjikist[]> (`/tags/${ id }/deerjikists`, data, { locale }))
qc.invalidateQueries ({ queryKey: tagsKeys.root })
toast ({ description: '更新しました.' })
toast ({ description: label ('messages.updated') })
}
catch (e)
{
applyValidationError (e)
toast ({ title: '更新失敗', description: '入力内容を確認してください.' })
toast ({ title: label ('messages.updateFailed'),
description: label ('messages.checkInputContent') })
}
finally
{
@@ -106,7 +109,7 @@ const DeerjikistDetailPage: FC = () => {
{/* プラットフォーム */}
<FormField
label="プラットフォーム"
label={label ('labels.platform')}
messages={fieldErrors[`deerjikists${ i }Platform`]}>
{({ describedBy, invalid }) => (
<select
@@ -131,7 +134,7 @@ const DeerjikistDetailPage: FC = () => {
{/* コード */}
<FormField
label="コード"
label={label ('labels.code')}
messages={fieldErrors[`deerjikists${ i }Code`]}>
{({ describedBy, invalid }) => (
<input
@@ -166,7 +169,7 @@ const DeerjikistDetailPage: FC = () => {
(disabled
? 'text-gray-300 bg-gray-500'
: 'text-white bg-blue-500'))}>
更新
{label ('actions.update')}
</button>
</div>
</form>
+10 -8
ファイルの表示
@@ -11,6 +11,7 @@ import { SITE_TITLE } from '@/config'
import { getHelpChanges, helpEditionDate } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { helpKeys } from '@/lib/queryKeys'
import { LIST_LIMIT_OPTIONS } from '@/lib/settings'
import { dateString } from '@/lib/utils'
@@ -19,6 +20,7 @@ import type { FC } from 'react'
const HelpChangesPage: FC = () => {
const { message, label } = useUITranslation ('help')
const language = localeLanguage (useLocale ())
const [searchParams, setSearchParams] = useSearchParams ()
const identifier = searchParams.get ('identifier') ?? ''
@@ -51,12 +53,12 @@ const HelpChangesPage: FC = () => {
return (
<MainArea className="min-w-0 max-w-full">
<Helmet>
<title>{`ヘルプ改定履歴 | ${ SITE_TITLE }`}</title>
<title>{message ('history').text} | {SITE_TITLE}</title>
</Helmet>
<div className="flex flex-wrap items-center justify-between gap-3">
<PageTitle>ヘルプ改定履歴</PageTitle>
<PageTitle>{label ('history')}</PageTitle>
<label className="flex items-center gap-2 text-xs text-muted-foreground">
<span>件数</span>
<span>{label ('common:countLabel')}</span>
<select value={limit} onChange={ev => handleLimit (ev.target.value)}
className="rounded border border-border bg-background px-2 py-1 text-sm
text-foreground">
@@ -67,7 +69,7 @@ const HelpChangesPage: FC = () => {
</div>
{isLoading
? <p className="mt-4">読み込み中...</p>
? <p className="mt-4">{label ('shared:messages.loading')}</p>
: (error
? <p className="mt-4 text-red-600" role="alert">{error.message}</p>
: (
@@ -77,9 +79,9 @@ const HelpChangesPage: FC = () => {
border-collapse">
<thead className="border-b-2 border-black dark:border-white">
<tr>
<th className="p-2 text-left">差分</th>
<th className="p-2 text-left">タイトル</th>
<th className="p-2 text-left">有効開始年月日</th>
<th className="p-2 text-left">{label ('shared:actions.diff')}</th>
<th className="p-2 text-left">{label ('shared:labels.title')}</th>
<th className="p-2 text-left">{label ('effectiveSince')}</th>
</tr>
</thead>
<tbody>
@@ -97,7 +99,7 @@ const HelpChangesPage: FC = () => {
<td className="p-2">
{entry.pred != null && (
<PrefetchLink to={diffTo}>
差分
{label ('shared:actions.diff')}
</PrefetchLink>)}
</td>
<td className="p-2">
+10 -6
ファイルの表示
@@ -12,6 +12,7 @@ import { getHelpArticle,
resolveHelpLanguage } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { dateString } from '@/lib/utils'
import { helpKeys } from '@/lib/queryKeys'
import NotFound from '@/pages/NotFound'
@@ -20,6 +21,7 @@ import type { FC } from 'react'
const HelpDetailPage: FC = () => {
const { message, label } = useUITranslation ('help')
const locale = useLocale ()
const { identifier = '' } = useParams ()
const location = useLocation ()
@@ -71,7 +73,9 @@ const HelpDetailPage: FC = () => {
return (
<MainArea>
<Helmet>
<title>{current?.title || identifier} ヘルプ | {SITE_TITLE}</title>
<title>
{message ('articleTitle', { title: current?.title || identifier }).text} | {SITE_TITLE}
</title>
</Helmet>
{requestedEdition && (
@@ -81,9 +85,9 @@ const HelpDetailPage: FC = () => {
? (
<PrefetchLink
to={`/help/${ encodeURIComponent (identifier) }?edition=${ pred }`}>
&lt; 古
{label ('shared:actions.old')}
</PrefetchLink>)
: '(最古)'}
: label ('shared:actions.oldest')}
<span>
{editionDate != null && dateString (editionDate, 'hour')}
@@ -94,13 +98,13 @@ const HelpDetailPage: FC = () => {
? (
<PrefetchLink
to={`/help/${ encodeURIComponent (identifier) }?edition=${ succ }`}>
新 &gt;
{label ('shared:actions.new')}
</PrefetchLink>)
: '(最新)'}
: label ('shared:actions.latest')}
</div>)}
<article lang={language} className="prose dark:prose-invert mx-auto p-4">
{Content ? <Content/> : <div>Loading...</div>}
{Content ? <Content/> : <div>{label ('common:loading')}</div>}
</article>
</MainArea>)
}
+3 -1
ファイルの表示
@@ -9,6 +9,7 @@ import { SITE_TITLE } from '@/config'
import { getHelpArticle, getHelpDiff, resolveHelpLanguage } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { helpKeys } from '@/lib/queryKeys'
import { cn } from '@/lib/utils'
import NotFound from '@/pages/NotFound'
@@ -19,6 +20,7 @@ const markers = { added: '+ ', removed: '- ', unchanged: ' ' }
const HelpDiffPage: FC = () => {
const { message } = useUITranslation ('help')
const locale = useLocale ()
const { identifier = '' } = useParams ()
const [searchParams] = useSearchParams ()
@@ -47,7 +49,7 @@ const HelpDiffPage: FC = () => {
return (
<MainArea className="min-w-0 max-w-full">
<Helmet>
<title>{`ヘルプ 差分: ${ title } | ${ SITE_TITLE }`}</title>
<title>{message ('diffTitle', { title }).text} | {SITE_TITLE}</title>
</Helmet>
<PageTitle>{title}</PageTitle>
+17 -13
ファイルの表示
@@ -17,6 +17,7 @@ import { compareHelpEditions,
normaliseHelpSearchText } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { helpKeys } from '@/lib/queryKeys'
import { LIST_LIMIT_OPTIONS } from '@/lib/settings'
import { dateString, inputClass } from '@/lib/utils'
@@ -32,6 +33,7 @@ const defaultDirection: Record<HelpOrderField, 'asc' | 'desc'> = {
const HelpSearchPage: FC = () => {
const { message, label } = useUITranslation ('help')
const language = localeLanguage (useLocale ())
const [searchParams, setSearchParams] = useSearchParams ()
const pageValue = searchParams.get ('page') ?? '1'
@@ -111,14 +113,14 @@ const HelpSearchPage: FC = () => {
return (
<MainArea className="min-w-0 max-w-full">
<Helmet>
<title>{`ヘルプ | ${ SITE_TITLE }`}</title>
<title>{message ('title').text} | {SITE_TITLE}</title>
</Helmet>
<div className="max-w-xl">
<div className="flex flex-wrap items-center justify-between gap-3">
<PageTitle>ヘルプ検索</PageTitle>
<PageTitle>{label ('search')}</PageTitle>
<label className="flex items-center gap-2 text-xs text-muted-foreground">
<span>件数</span>
<span>{label ('common:countLabel')}</span>
<select value={limit} onChange={ev => handleLimit (ev.target.value)}
className="rounded border border-border bg-background px-2 py-1 text-sm
text-foreground">
@@ -128,19 +130,19 @@ const HelpSearchPage: FC = () => {
</label>
</div>
<form onSubmit={handleSearch} className="space-y-2">
<FormField label="タイトル">
<FormField label={label ('shared:labels.title')}>
{({ invalid }) => (
<input type="text"
aria-label="タイトル"
aria-label={message ('shared:labels.title').text}
value={title}
onChange={e => setTitle (e.target.value)}
className={inputClass (invalid)}/>)}
</FormField>
<FormField label="内容">
<FormField label={label ('shared:labels.content')}>
{({ invalid }) => (
<input type="text"
aria-label="内容"
aria-label={message ('shared:labels.content').text}
value={text}
onChange={e => setText (e.target.value)}
className={inputClass (invalid)}/>)}
@@ -149,19 +151,21 @@ const HelpSearchPage: FC = () => {
<div className="py-3">
<button type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded">
検索
{label ('common:search')}
</button>
</div>
</form>
</div>
{loading
? <p className="mt-4">読み込み中...</p>
? <p className="mt-4">{label ('shared:messages.loading')}</p>
: (error
? <p className="mt-4 text-red-600" role="alert">{error.message}</p>
: (
<div className="mt-4">
<p className="mb-2 text-sm" aria-live="polite">{results.length} 件</p>
<p className="mb-2 text-sm" aria-live="polite">
{label ('shared:labels.resultsCount', { count: results.length })}
</p>
<div className="min-w-0 max-w-full overflow-x-auto">
<table className="table-auto w-full min-w-[640px] whitespace-nowrap
border-collapse text-gray-900 dark:text-gray-100">
@@ -170,14 +174,14 @@ const HelpSearchPage: FC = () => {
<th className="p-2 text-left">
<SortHeader<HelpOrderField>
by="title"
label="タイトル"
label={label ('shared:labels.title')}
currentOrder={order}
defaultDirection={defaultDirection}/>
</th>
<th className="p-2 text-left">
<SortHeader<HelpOrderField>
by="updated_at"
label="有効開始年月日"
label={label ('effectiveSince')}
currentOrder={order}
defaultDirection={defaultDirection}/>
</th>
@@ -206,7 +210,7 @@ const HelpSearchPage: FC = () => {
</table>
</div>
{totalPages > 0 && <Pagination page={page} totalPages={totalPages}/>}
{results.length === 0 && <p className="mt-4">該当する記事がありません.</p>}
{results.length === 0 && <p className="mt-4">{label ('empty')}</p>}
</div>))}
</MainArea>)
}
+4 -4
ファイルの表示
@@ -8,7 +8,6 @@ import LocalisedText from '@/components/LocalisedText'
import PrefetchLink from '@/components/PrefetchLink'
import SortHeader from '@/components/SortHeader'
import TagLink from '@/components/TagLink'
import TranslatedText from '@/components/TranslatedText'
import DateTimeField from '@/components/common/DateTimeField'
import FormField from '@/components/common/FormField'
import PageTitle from '@/components/common/PageTitle'
@@ -76,8 +75,7 @@ const parseOrder = (value: string | null): FetchTagsOrder | null => {
const TagListPage: FC = () => {
const locale = useLocale ()
const { message } = useUITranslation ('tags')
const label = (key: string) => <TranslatedText namespace="tags" messageKey={key}/>
const { message, label } = useUITranslation ('tags')
const behaviourSettings = useClientBehaviourSettings ()
const animationMode = behaviourSettings.animation ?? 'normal'
const location = useLocation ()
@@ -265,7 +263,9 @@ const TagListPage: FC = () => {
className={inputClass (invalid)}>
<option value="">&nbsp;</option>
{CATEGORIES.map (cat => (
<option key={cat} value={cat}
<option
key={cat}
value={cat}
lang={message (`common:categories.${ cat }`).language}>
{message (`common:categories.${ cat }`).text}
</option>))}
+11 -8
ファイルの表示
@@ -4,6 +4,7 @@ import { Helmet } from 'react-helmet-async'
import { useLocation, useParams } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import PostList from '@/components/PostList'
import PrefetchLink from '@/components/PrefetchLink'
import TagLink from '@/components/TagLink'
@@ -25,6 +26,7 @@ import type { Tag } from '@/types'
const WikiDetailPage: FC = () => {
const { message, label } = useUITranslation ('shared')
const locale = useLocale ()
const params = useParams ()
const title = params.title ?? ''
@@ -128,7 +130,8 @@ const WikiDetailPage: FC = () => {
return (
<MainArea>
<Helmet>
<title>{`${ effectiveTitle }${ deprecated ? '(廃止)' : '' } Wiki | ${ SITE_TITLE }`}</title>
<title>{`${ effectiveTitle }${ deprecated
? message ('labels.deprecatedSuffix').text : '' } Wiki | ${ SITE_TITLE }`}</title>
{!(wikiPage?.body) && <meta name="robots" content="noindex"/>}
</Helmet>
@@ -137,15 +140,15 @@ const WikiDetailPage: FC = () => {
border border-gray-700 rounded px-2 py-1 mb-4">
{wikiPage.pred ? (
<PrefetchLink to={`/wiki/${ encodeURIComponent (title) }?version=${ wikiPage.pred }`}>
&lt; 古
</PrefetchLink>) : '(最古)'}
{label ('actions.old')}
</PrefetchLink>) : label ('actions.oldest')}
<span>{wikiPage.updatedAt}</span>
{wikiPage.succ ? (
<PrefetchLink to={`/wiki/${ encodeURIComponent (title) }?version=${ wikiPage.succ }`}>
新 &gt;
</PrefetchLink>) : '(最新)'}
{label ('actions.new')}
</PrefetchLink>) : label ('actions.latest')}
</div>)}
<article className="prose dark:prose-invert mx-auto p-4">
@@ -156,14 +159,14 @@ const WikiDetailPage: FC = () => {
withWiki={false}
withCount={false}
{...(version && { to: `/wiki/${ encodeURIComponent (title) }` })}/>
{deprecated && <span>(廃止)</span>}
{deprecated && label ('labels.deprecatedSuffix')}
</h1>
{waiting ? <div>Loading...</div> : <WikiBody title={title} body={wikiPage?.body}/>}
{waiting ? label ('common:loading') : <WikiBody title={title} body={wikiPage?.body}/>}
{(!(version) && posts.length > 0) && (
<div className="not-prose">
<TabGroup>
<Tab name="広場">
<Tab name={message ('navigation:posts').text}>
<PostList posts={posts}/>
</Tab>
</TabGroup>
+6 -3
ファイルの表示
@@ -5,6 +5,7 @@ import { Helmet } from 'react-helmet-async'
import { useLocation, useParams } from 'react-router-dom'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import PageTitle from '@/components/common/PageTitle'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
@@ -15,6 +16,7 @@ import type { WikiPageDiff } from '@/types'
const WikiDiffPage: FC = () => {
const { message, label } = useUITranslation ('wiki')
const locale = useLocale ()
const { id } = useParams ()
@@ -26,7 +28,8 @@ const WikiDiffPage: FC = () => {
const from = query.get ('from')
const to = query.get ('to')
const displayTitle = diff
? `${ diff.title }${ diff.deprecatedAt != null ? '(廃止)' : '' }`
? `${ diff.title }${ diff.deprecatedAt != null
? message ('shared:labels.deprecatedSuffix').text : '' }`
: ''
useEffect (() => {
@@ -38,7 +41,7 @@ const WikiDiffPage: FC = () => {
return (
<MainArea>
<Helmet>
<title>{`Wiki 差分: ${ displayTitle } | ${ SITE_TITLE }`}</title>
<title>{message ('diffTitle', { title: displayTitle }).text} | {SITE_TITLE}</title>
</Helmet>
<PageTitle>{displayTitle}</PageTitle>
<div className="prose mx-auto p-4">
@@ -49,7 +52,7 @@ const WikiDiffPage: FC = () => {
d.type === 'removed' && 'bg-red-200 dark:bg-red-800')}>
{d.content}
</p>)))
: 'Loading...'}
: label ('common:loading')}
</div>
</MainArea>)
}
+11 -8
ファイルの表示
@@ -6,6 +6,7 @@ import MdEditor from 'react-markdown-editor-lite'
import { useParams } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import FieldError from '@/components/common/FieldError'
import FormField from '@/components/common/FormField'
import MainArea from '@/components/layout/MainArea'
@@ -37,6 +38,7 @@ type WikiFormField = 'title' | 'body'
const WikiEditPage: FC<Props> = ({ user }) => {
const { message, label } = useUITranslation ('wiki')
const locale = useLocale ()
const editable = canEditContent (user)
@@ -66,13 +68,14 @@ const WikiEditPage: FC<Props> = ({ user }) => {
qc.setQueryData (wikiKeys.show (title, { }, locale),
(prev: WikiPage) => ({ ...prev, title, body }))
qc.invalidateQueries ({ queryKey: wikiKeys.root })
toast ({ title: '投稿成功!' })
toast ({ title: label ('shared:messages.postSuccess') })
navigate (`/wiki/${ title }`)
}
catch (e)
{
applyValidationError (e)
toast ({ title: '投稿失敗', description: '入力を確認してください。' })
toast ({ title: label ('shared:messages.postFailed'),
description: label ('shared:messages.checkInputModern') })
}
}
@@ -95,18 +98,18 @@ const WikiEditPage: FC<Props> = ({ user }) => {
return (
<MainArea>
<Helmet>
<title>{`Wiki ページを編輯 | ${ SITE_TITLE }`}</title>
<title>{`${ message ('editPage').text } | ${ SITE_TITLE }`}</title>
</Helmet>
<div className="max-w-xl mx-auto p-4 space-y-4">
<h1 className="text-2xl font-bold mb-2">Wiki ページを編輯</h1>
<h1 className="text-2xl font-bold mb-2">{label ('editPage')}</h1>
{loading ? 'Loading...' : (
{loading ? label ('common:loading') : (
<>
<FieldError messages={baseErrors}/>
{/* タイトル */}
{/* TODO: タグ補完 */}
<FormField label="タイトル" messages={fieldErrors.title}>
<FormField label={label ('shared:labels.title')} messages={fieldErrors.title}>
{({ describedBy, invalid }) => (
<input type="text"
value={title}
@@ -117,7 +120,7 @@ const WikiEditPage: FC<Props> = ({ user }) => {
</FormField>
{/* 本文 */}
<FormField label="本文" messages={fieldErrors.body}>
<FormField label={label ('shared:labels.body')} messages={fieldErrors.body}>
{() => (
<MdEditor value={body}
style={{ height: '500px' }}
@@ -129,7 +132,7 @@ const WikiEditPage: FC<Props> = ({ user }) => {
{/* 送信 */}
<button onClick={handleSubmit}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400">
編輯
{label ('shared:actions.edit')}
</button>
</>)}
</div>
+10 -8
ファイルの表示
@@ -5,6 +5,7 @@ import { Helmet } from 'react-helmet-async'
import { useLocation } from 'react-router-dom'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import PrefetchLink from '@/components/PrefetchLink'
import PageTitle from '@/components/common/PageTitle'
import MainArea from '@/components/layout/MainArea'
@@ -16,6 +17,7 @@ import type { WikiPageChange } from '@/types'
const WikiHistoryPage: FC = () => {
const { message, label } = useUITranslation ('wiki')
const locale = useLocale ()
const [changes, setChanges] = useState<WikiPageChange[]> ([])
@@ -32,18 +34,18 @@ const WikiHistoryPage: FC = () => {
return (
<MainArea>
<Helmet>
<title>{`Wiki 変更履歴 | ${ SITE_TITLE }`}</title>
<title>{message ('history').text} | {SITE_TITLE}</title>
</Helmet>
<PageTitle>Wiki 履歴</PageTitle>
<PageTitle>{label ('historyHeading')}</PageTitle>
<table className="table-auto w-full border-collapse">
<thead className="border-b-2 border-black dark:border-white">
<tr>
<th></th>
<th className="p-2 text-left">タイトル</th>
<th className="p-2 text-left">変更</th>
<th className="p-2 text-left">日時</th>
<th className="p-2 text-left">{label ('shared:labels.title')}</th>
<th className="p-2 text-left">{label ('change')}</th>
<th className="p-2 text-left">{label ('dateTime')}</th>
</tr>
</thead>
<tbody>
@@ -53,7 +55,7 @@ const WikiHistoryPage: FC = () => {
{change.pred != null && (
<PrefetchLink
to={`/wiki/${ change.wikiPage.id }/diff?from=${ change.pred }&to=${ change.revisionId }`}>
差分
{label ('shared:actions.diff')}
</PrefetchLink>)}
</td>
<td className="p-2">
@@ -61,10 +63,10 @@ const WikiHistoryPage: FC = () => {
to={`/wiki/${ encodeURIComponent (change.wikiPage.title) }?version=${ change.revisionId }`}>
{change.wikiPage.title}
</PrefetchLink>
{change.wikiPage.deprecatedAt != null && <span>(廃止)</span>}
{change.wikiPage.deprecatedAt != null && label ('shared:labels.deprecatedSuffix')}
</td>
<td className="p-2">
{change.pred == null ? '新規' : '更新'}
{change.pred == null ? label ('navigation:create') : label ('shared:actions.update')}
</td>
<td className="p-2">
<PrefetchLink to={`/users/${ change.user.id }`}>
+10 -7
ファイルの表示
@@ -7,6 +7,7 @@ import { useLocation } from 'react-router-dom'
import 'react-markdown-editor-lite/lib/index.css'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import FieldError from '@/components/common/FieldError'
import FormField from '@/components/common/FormField'
import MainArea from '@/components/layout/MainArea'
@@ -36,6 +37,7 @@ type WikiFormField = 'title' | 'body'
const WikiNewPage: FC<Props> = ({ user }) => {
const { message, label } = useUITranslation ('wiki')
const locale = useLocale ()
const editable = canEditContent (user)
@@ -67,13 +69,14 @@ const WikiNewPage: FC<Props> = ({ user }) => {
await Promise.all ([
qc.invalidateQueries ({ queryKey: wikiKeys.root }),
qc.invalidateQueries ({ queryKey: tagsKeys.root })])
toast ({ title: '投稿成功!' })
toast ({ title: label ('shared:messages.postSuccess') })
navigate (`/wiki/${ encodeURIComponent (data.title) }`)
}
catch (e)
{
applyValidationError (e)
toast ({ title: '投稿失敗', description: '入力を確認してください。' })
toast ({ title: label ('shared:messages.postFailed'),
description: label ('shared:messages.checkInputModern') })
}
}
@@ -83,15 +86,15 @@ const WikiNewPage: FC<Props> = ({ user }) => {
return (
<MainArea>
<Helmet>
<title>{`新規 Wiki ページ | ${ SITE_TITLE }`}</title>
<title>{`${ message ('newPage').text } | ${ SITE_TITLE }`}</title>
</Helmet>
<div className="max-w-xl mx-auto p-4 space-y-4">
<h1 className="text-2xl font-bold mb-2">新規 Wiki ページ</h1>
<h1 className="text-2xl font-bold mb-2">{label ('newPage')}</h1>
<FieldError messages={baseErrors}/>
{/* タイトル */}
{/* TODO: タグ補完 */}
<FormField label="タイトル" messages={fieldErrors.title}>
<FormField label={label ('shared:labels.title')} messages={fieldErrors.title}>
{({ describedBy, invalid }) => (
<input type="text"
value={title}
@@ -102,7 +105,7 @@ const WikiNewPage: FC<Props> = ({ user }) => {
</FormField>
{/* 本文 */}
<FormField label="本文" messages={fieldErrors.body}>
<FormField label={label ('shared:labels.body')} messages={fieldErrors.body}>
{() => (
<MdEditor value={body}
style={{ height: '500px' }}
@@ -114,7 +117,7 @@ const WikiNewPage: FC<Props> = ({ user }) => {
{/* 送信 */}
<button onClick={handleSubmit}
className="px-4 py-2 bg-blue-600 text-white rounded disabled:bg-gray-400">
追加
{label ('shared:actions.add')}
</button>
</div>
</MainArea>)
+8 -6
ファイルの表示
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import PrefetchLink from '@/components/PrefetchLink'
import FormField from '@/components/common/FormField'
import PageTitle from '@/components/common/PageTitle'
@@ -16,6 +17,7 @@ import type { WikiPage } from '@/types'
const WikiSearchPage: FC = () => {
const { label } = useUITranslation ('shared')
const locale = useLocale ()
const [title, setTitle] = useState ('')
const [text, setText] = useState ('')
@@ -46,7 +48,7 @@ const WikiSearchPage: FC = () => {
<PageTitle>Wiki</PageTitle>
<form onSubmit={handleSearch} className="space-y-2">
{/* タイトル */}
<FormField label="タイトル">
<FormField label={label ('labels.title')}>
{({ invalid }) => (
<input type="text"
value={title}
@@ -55,7 +57,7 @@ const WikiSearchPage: FC = () => {
</FormField>
{/* 内容 */}
<FormField label="内容">
<FormField label={label ('labels.content')}>
{({ invalid }) => (
<input type="text"
value={text}
@@ -67,7 +69,7 @@ const WikiSearchPage: FC = () => {
<div className="py-3">
<button type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded">
検索
{label ('common:search')}
</button>
</div>
</form>
@@ -77,8 +79,8 @@ const WikiSearchPage: FC = () => {
<table className="table-auto w-full border-collapse">
<thead className="border-b-2 border-black dark:border-white">
<tr>
<th className="p-2 text-left">タイトル</th>
<th className="p-2 text-left">最終更新</th>
<th className="p-2 text-left">{label ('labels.title')}</th>
<th className="p-2 text-left">{label ('labels.lastUpdated')}</th>
</tr>
</thead>
<tbody>
@@ -89,7 +91,7 @@ const WikiSearchPage: FC = () => {
<PrefetchLink to={`/wiki/${ encodeURIComponent (page.title) }`}>
{page.title}
</PrefetchLink>
{page.deprecatedAt != null && <span>(廃止)</span>}
{page.deprecatedAt != null && label ('labels.deprecatedSuffix')}
</td>
<td className="p-2">
{dateString (page.updatedAt)}