AI: address issue #455
このコミットが含まれているのは:
@@ -7,5 +7,5 @@ Font copyright: Copyright 2018 Google Inc. All Rights Reserved.
|
||||
- License: SIL Open Font License 1.1; see the accompanying LICENSE.
|
||||
- Retrieved: 2026-11-08.
|
||||
|
||||
Used only by the Unicode Mongolian sample in the development layout route.
|
||||
Used by the Mongolian UI at `/mn-Mong`.
|
||||
The font and license are served together from this directory.
|
||||
|
||||
+15
-9
@@ -26,7 +26,7 @@ import { isAuthorityDependentQuery, localesKeys, sessionKeys } from '@/lib/query
|
||||
import { isCountsPath } from '@/lib/routeLocales'
|
||||
import { switchPostFilterLocale } from '@/lib/posts'
|
||||
import { switchWikiLocale } from '@/lib/wiki'
|
||||
import { useLocale, useVerticalPreview } from '@/lib/useLocale'
|
||||
import { useLocale, useUILocale, useVerticalPreview } from '@/lib/useLocale'
|
||||
import { applyClientAnimationMode,
|
||||
applyClientAppearance,
|
||||
fetchUserThemeSlots,
|
||||
@@ -46,7 +46,7 @@ import ServiceUnavailable from '@/pages/ServiceUnavailable'
|
||||
import type { ComponentType, Dispatch, FC, ReactNode, SetStateAction } from 'react'
|
||||
import type { RouteMatch } from 'react-router-dom'
|
||||
|
||||
import type { AvailableLocale, LocaleCode } from '@/lib/locales'
|
||||
import type { AvailableLocale, LocaleCode, UILocaleCode } from '@/lib/locales'
|
||||
import type { PageRoute } from '@/lib/routeLocales'
|
||||
import type { User } from '@/types'
|
||||
|
||||
@@ -272,11 +272,16 @@ const RoutedApp = ({ locales }: { locales: AvailableLocale[] }) => {
|
||||
const robots = isCountsPath (location.pathname) ? 'noindex' : route?.route.handle?.robots
|
||||
const pagePath = unlocalisedPath (location.pathname)
|
||||
const countsPage = pagePath === '/counts' || pagePath.startsWith ('/counts/')
|
||||
const switchLocale = async (targetLocale: LocaleCode): Promise<string> => {
|
||||
const switchLocale = async (target: UILocaleCode): Promise<string> => {
|
||||
const targetLocale = target === 'mn-Mong' ? 'ja' : target
|
||||
if (targetLocale === locale)
|
||||
return switchLocalePath (location, target)
|
||||
const resolve = route?.route.handle?.switchLocale
|
||||
return (resolve != null && route != null)
|
||||
? resolve ({ location, params: route.params, locale, targetLocale })
|
||||
const destination =
|
||||
(resolve != null && route != null)
|
||||
? await resolve ({ location, params: route.params, locale, targetLocale })
|
||||
: switchLocalePath (location, targetLocale)
|
||||
return target === 'mn-Mong' ? `/mn-Mong${unlocalisedPath (destination)}` : destination
|
||||
}
|
||||
const [user, setUserState] = useState<User | null> (
|
||||
() => client.getQueryData<User> (sessionKeys.current) ?? null)
|
||||
@@ -413,6 +418,7 @@ const RoutedApp = ({ locales }: { locales: AvailableLocale[] }) => {
|
||||
|
||||
const LocaleRoute: FC = () => {
|
||||
const { locale } = useParams ()
|
||||
const uiLocale = useUILocale ()
|
||||
const location = useLocation ()
|
||||
const verticalPreview = isVerticalPreviewPath (location.pathname)
|
||||
const { i18n } = useTranslation ()
|
||||
@@ -433,10 +439,10 @@ const LocaleRoute: FC = () => {
|
||||
|
||||
useLayoutEffect (() => {
|
||||
if (activeLocale != null)
|
||||
document.documentElement.lang = activeLocale
|
||||
if (activeLocale != null && i18n.language !== activeLocale)
|
||||
void i18n.changeLanguage (activeLocale)
|
||||
}, [activeLocale, i18n])
|
||||
document.documentElement.lang = uiLocale
|
||||
if (activeLocale != null && i18n.language !== uiLocale)
|
||||
void i18n.changeLanguage (uiLocale)
|
||||
}, [activeLocale, uiLocale, i18n])
|
||||
|
||||
const redirect = legacyLocalePath (location, pathname => resolvePageRoute (pathname) != null)
|
||||
if (!(isLocaleCode (locale)) && !verticalPreview && redirect == null)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Globe } from 'lucide-react'
|
||||
import { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useLocale, useLocaleNavigate, useVerticalPreview } from '@/lib/useLocale'
|
||||
import { useLocale, useLocaleNavigate, useUILocale, useVerticalPreview } from '@/lib/useLocale'
|
||||
import LocalisedText, { formatUIString } from '@/components/LocalisedText'
|
||||
import Separator from '@/components/MenuSeparator'
|
||||
import PrefetchLink from '@/components/PrefetchLink'
|
||||
@@ -13,8 +13,10 @@ import TranslatedText from '@/components/TranslatedText'
|
||||
import { toast } from '@/components/ui/use-toast'
|
||||
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
||||
import { isLocaleCode,
|
||||
localeFromPath,
|
||||
isUILocaleCode,
|
||||
MONGOLIAN_LOCALE,
|
||||
switchLocalePath,
|
||||
uiLocaleFromPath,
|
||||
unlocalisedPath } from '@/lib/locales'
|
||||
import { uiMessage, uiMessageLanguage } from '@/lib/messages'
|
||||
import { useUITranslation } from '@/lib/useUITranslation'
|
||||
@@ -28,13 +30,13 @@ import { fetchWikiPage } from '@/lib/wiki'
|
||||
|
||||
import type { FC, MouseEvent } from 'react'
|
||||
|
||||
import type { AvailableLocale, LocaleCode } from '@/lib/locales'
|
||||
import type { AvailableLocale, UILocaleCode } from '@/lib/locales'
|
||||
import type { Material, Menu, MenuVisibleItem, Tag, User } from '@/types'
|
||||
|
||||
type Props = {
|
||||
user: User | null
|
||||
locales: AvailableLocale[]
|
||||
resolveLocaleSwitch?: (locale: LocaleCode) => Promise<string> }
|
||||
resolveLocaleSwitch?: (locale: UILocaleCode) => Promise<string> }
|
||||
type NavDensity = 'auto' | 'comfortable' | 'compact'
|
||||
|
||||
|
||||
@@ -47,7 +49,7 @@ export const menuOutline = (
|
||||
pathName: string },
|
||||
): Menu => {
|
||||
const pathName = unlocalisedPath (fullPathname)
|
||||
const locale = localeFromPath (fullPathname) ?? 'ja'
|
||||
const locale = uiLocaleFromPath (fullPathname)
|
||||
|
||||
const message = (
|
||||
key: Parameters<typeof uiMessage>[1],
|
||||
@@ -137,8 +139,11 @@ export const menuOutline = (
|
||||
const TopNav: FC<Props> = ({ user, locales, resolveLocaleSwitch }) => {
|
||||
const { message, label } = useUITranslation ('shared')
|
||||
const locale = useLocale ()
|
||||
const uiLocale = useUILocale ()
|
||||
const verticalPreview = useVerticalPreview ()
|
||||
const localeName = locales.find (entry => entry.code === locale)?.name
|
||||
const uiLocales = [...locales.filter (entry => isLocaleCode (entry.code)),
|
||||
...(locales.some (entry => entry.code === 'ja') ? [MONGOLIAN_LOCALE] : [])]
|
||||
const localeName = uiLocales.find (entry => entry.code === uiLocale)?.name
|
||||
const navigate = useLocaleNavigate ()
|
||||
const location = useLocation ()
|
||||
const currentLocation = useRef (location)
|
||||
@@ -443,17 +448,18 @@ const TopNav: FC<Props> = ({ user, locales, resolveLocaleSwitch }) => {
|
||||
rounded px-1 focus-within:ring-2">
|
||||
<Globe aria-hidden="true" size={16}/>
|
||||
<span className="top-nav-locale-name whitespace-nowrap text-sm">
|
||||
<LocalisedText language={locale}>{localeName}</LocalisedText>
|
||||
<LocalisedText language={uiLocale}>{localeName}</LocalisedText>
|
||||
</span>
|
||||
<select
|
||||
aria-label={formatUIString (localeName ?? '', locale)}
|
||||
value={locale}
|
||||
aria-label={formatUIString (localeName ?? '', uiLocale)}
|
||||
lang={uiLocale}
|
||||
value={uiLocale}
|
||||
disabled={switchingLocale}
|
||||
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
||||
onChange={async event => {
|
||||
const nextLocale = event.target.value
|
||||
if (isLocaleCode (nextLocale)
|
||||
&& locales.some (entry => entry.code === nextLocale))
|
||||
if (isUILocaleCode (nextLocale)
|
||||
&& uiLocales.some (entry => entry.code === nextLocale))
|
||||
{
|
||||
const request = ++localeSwitchRequest.current
|
||||
setSwitchingLocale (true)
|
||||
@@ -478,8 +484,8 @@ const TopNav: FC<Props> = ({ user, locales, resolveLocaleSwitch }) => {
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{locales.map (entry => isLocaleCode (entry.code) && (
|
||||
<option key={entry.code} value={entry.code}>
|
||||
{uiLocales.map (entry => (
|
||||
<option key={entry.code} value={entry.code} lang={entry.code}>
|
||||
{formatUIString (entry.name, entry.code)}
|
||||
</option>))}
|
||||
</select>
|
||||
@@ -489,7 +495,7 @@ const TopNav: FC<Props> = ({ user, locales, resolveLocaleSwitch }) => {
|
||||
<div ref={layoutRef} className="top-nav-layout w-full min-w-0 shrink-0"
|
||||
data-desktop={desktop ?? undefined} data-density={density}
|
||||
data-layout-ready={layoutReady}>
|
||||
<nav ref={barRef} lang={uiMessageLanguage (locale, 'posts')}
|
||||
<nav ref={barRef} lang={uiMessageLanguage (uiLocale, 'posts')}
|
||||
className="top-nav-root top-nav-bar flex justify-between items-center w-full">
|
||||
<div className="top-nav-primary flex shrink-0 items-center h-12">
|
||||
<PrefetchLink
|
||||
@@ -560,18 +566,10 @@ const TopNav: FC<Props> = ({ user, locales, resolveLocaleSwitch }) => {
|
||||
<div className="top-nav-account ml-auto flex min-w-0 items-center gap-2">
|
||||
<TopNavUser user={user}/>
|
||||
{localeSelector}
|
||||
{verticalPreview && (
|
||||
<span className="vertical-preview-sample" lang="mn-Mong">
|
||||
ᠮᠣᠩᠭᠣᠯ
|
||||
</span>)}
|
||||
</div>
|
||||
|
||||
<div className="top-nav-mobile-controls ml-auto flex shrink-0 items-center gap-2">
|
||||
{menuOpen && localeSelector}
|
||||
{verticalPreview && (
|
||||
<span className="vertical-preview-sample" lang="mn-Mong">
|
||||
ᠮᠣᠩᠭᠣᠯ
|
||||
</span>)}
|
||||
<button
|
||||
type="button"
|
||||
className="top-nav-mobile-toggle shrink-0 whitespace-nowrap
|
||||
|
||||
@@ -8,7 +8,7 @@ type Props = {
|
||||
|
||||
|
||||
const Form: FC<Props> = ({ children, className }) => (
|
||||
<div className={cn ('mx-auto max-w-xl space-y-4 p-4', className)}>
|
||||
<div className={cn ('form-layout mx-auto max-w-xl space-y-4 p-4', className)}>
|
||||
{children}
|
||||
</div>)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ type Props = {
|
||||
|
||||
|
||||
const MainArea: FC<Props> = (
|
||||
{ children, className, animateLayout = true, verticalContent = false },
|
||||
{ children, className, animateLayout = true, verticalContent = true },
|
||||
) => {
|
||||
const behaviourSettings = useClientBehaviourSettings ()
|
||||
const verticalPreview = useVerticalPreview ()
|
||||
|
||||
@@ -10,7 +10,7 @@ import { AppFrame, appRoutes, resolvePageRoute } from '@/App'
|
||||
import { apiGet, isApiError } from '@/lib/api'
|
||||
import { createUII18n } from '@/lib/i18n'
|
||||
import { fetchLocales, isLocaleCode, isVerticalPreviewPath,
|
||||
legacyLocalePath } from '@/lib/locales'
|
||||
legacyLocalePath, uiLocaleFromPath } from '@/lib/locales'
|
||||
import { localesKeys, sessionKeys } from '@/lib/queryKeys'
|
||||
import { withRenderContext } from '@/lib/renderContext.server'
|
||||
|
||||
@@ -78,6 +78,7 @@ export const render = async (
|
||||
if (legacy != null && known != null)
|
||||
return { status: 308, location: legacy }
|
||||
const locale = isLocaleCode (segment) ? segment : 'ja'
|
||||
const uiLocale = uiLocaleFromPath (pathname)
|
||||
const routePrefix = verticalPreview ? 'mn-Mong' : locale
|
||||
if (known?.route.handle?.redirectTo != null)
|
||||
return { status: 308,
|
||||
@@ -117,7 +118,7 @@ export const render = async (
|
||||
if (prepare != null && known != null && status === 200)
|
||||
status = await prepare ({ client, params: known.params, locale,
|
||||
searchParams: url.searchParams })
|
||||
const instance = createUII18n (locale)
|
||||
const instance = createUII18n (uiLocale)
|
||||
const handler = createStaticHandler (appRoutes)
|
||||
const context = await handler.query (new Request (url))
|
||||
if (context instanceof Response)
|
||||
@@ -187,11 +188,11 @@ export const render = async (
|
||||
isAxiosError: true, response: {
|
||||
status: isApiError (query.state.error) ? query.state.error.response?.status : 500 } } }
|
||||
})) }
|
||||
const bootstrap = JSON.stringify ({ state, status, locale,
|
||||
const bootstrap = JSON.stringify ({ state, status, locale: uiLocale,
|
||||
url: url.pathname + url.search })
|
||||
.replace (/</g, '\\u003c').replace (/\u2028/g, '\\u2028')
|
||||
.replace (/\u2029/g, '\\u2029')
|
||||
return { status, html, head, locale, bootstrap,
|
||||
return { status, html, head, locale: uiLocale, bootstrap,
|
||||
setCookies: requestContext.setCookies }
|
||||
}
|
||||
finally
|
||||
|
||||
+66
-5
@@ -626,17 +626,22 @@ body
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Loaded only by the development-only sample, with the OFL alongside the font. */
|
||||
/* Locally hosted Mongolian font; the OFL accompanies the font file. */
|
||||
@font-face
|
||||
{
|
||||
font-family: 'BTRC Mongolian Preview';
|
||||
font-family: 'BTRC Mongolian';
|
||||
src: url('/fonts/noto-sans-mongolian/NotoSansMongolian-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* Development-only /mn-Mong layout. Unannotated pages retain horizontal text. */
|
||||
/* Mongolian flows down each line, then into the next column on the right. */
|
||||
:lang(mn-Mong)
|
||||
{
|
||||
font-family: 'BTRC Mongolian', 'Mongolian Baiti', 'Noto Sans JP Variable', sans-serif;
|
||||
}
|
||||
|
||||
[data-vertical-preview]
|
||||
{
|
||||
min-width: 0;
|
||||
@@ -675,6 +680,56 @@ body
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
|
||||
/* Specialised lists below retain their own grid and table layouts. */
|
||||
[data-vertical-preview] .main-area[data-vertical-content]:not(
|
||||
.posts-main-area, .tags-main-area, .wiki-main-area)
|
||||
{
|
||||
writing-mode: vertical-lr;
|
||||
text-orientation: mixed;
|
||||
text-align: start;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .main-area[data-vertical-content]:not(
|
||||
.posts-main-area, .tags-main-area, .wiki-main-area) > :is(div, article, section)
|
||||
{
|
||||
max-width: none;
|
||||
max-inline-size: 100%;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .main-area[data-vertical-content] .prose
|
||||
{
|
||||
max-width: none;
|
||||
max-inline-size: 100%;
|
||||
}
|
||||
|
||||
/* Input and data widgets keep their physical coordinates and nested scrolling. */
|
||||
[data-vertical-preview] .main-area[data-vertical-content] :is(
|
||||
form, .form-layout, pre, .overflow-x-auto, .rc-md-editor, .not-prose)
|
||||
{
|
||||
writing-mode: horizontal-tb;
|
||||
text-orientation: mixed;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .main-area[data-vertical-content] :is(form, .form-layout)
|
||||
{
|
||||
width: min(36rem, calc(100vw - 2rem));
|
||||
}
|
||||
|
||||
[data-vertical-preview] :where(.main-area[data-vertical-content]) :is(
|
||||
button, input, select, textarea, code, time, table, img, video, iframe, canvas, svg)
|
||||
{
|
||||
writing-mode: horizontal-tb;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .main-area[data-vertical-content] :is(img, video, iframe, canvas)
|
||||
{
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .sidebar-panel
|
||||
{
|
||||
width: 100%;
|
||||
@@ -741,16 +796,22 @@ body
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .vertical-preview-sample
|
||||
[data-vertical-preview] .top-nav-locale-name:has(:lang(mn-Mong))
|
||||
{
|
||||
display: inline;
|
||||
writing-mode: vertical-lr;
|
||||
text-orientation: mixed;
|
||||
font-family: 'BTRC Mongolian Preview', 'Mongolian Baiti', sans-serif;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
[data-vertical-preview] .top-nav-menu-link:has(.top-nav-locale-name)
|
||||
{
|
||||
height: auto;
|
||||
min-height: 2rem;
|
||||
}
|
||||
|
||||
/* The post grid fills each column downwards before continuing to the right. */
|
||||
[data-vertical-preview] .posts-main-area > div
|
||||
{
|
||||
|
||||
+11
-4
@@ -1,9 +1,11 @@
|
||||
import { createInstance } from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
|
||||
import { MONGOLIAN_LOCALE, SUPPORTED_LOCALES } from '@/lib/locales'
|
||||
|
||||
import type { i18n as I18n, TOptions } from 'i18next'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { UILocaleCode } from '@/lib/locales'
|
||||
|
||||
// Keep Japanese placeholders editable without reporting them as completed translations.
|
||||
const translatedResource = (resource: Record<string, unknown>): Record<string, unknown> => {
|
||||
@@ -34,9 +36,14 @@ 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
|
||||
locale === 'ja' ? dictionary : translatedResource (dictionary)
|
||||
}
|
||||
const namespaces = Object.keys (resources.ja ?? { })
|
||||
// No Mongolian translations yet; preserve the existing fallback language metadata.
|
||||
resources[MONGOLIAN_LOCALE.code] ??= { }
|
||||
for (const namespace of namespaces)
|
||||
resources[MONGOLIAN_LOCALE.code][namespace] ??=
|
||||
translatedResource ({ _untranslated: ['*'] })
|
||||
|
||||
|
||||
export const createUII18n = (locale: string): I18n => {
|
||||
@@ -44,7 +51,7 @@ export const createUII18n = (locale: string): I18n => {
|
||||
void instance.init ({
|
||||
lng: locale,
|
||||
fallbackLng: 'ja',
|
||||
supportedLngs: ['ja', 'ain-Kana'],
|
||||
supportedLngs: [...SUPPORTED_LOCALES, MONGOLIAN_LOCALE.code],
|
||||
load: 'currentOnly',
|
||||
initAsync: false,
|
||||
defaultNS: 'navigation',
|
||||
@@ -122,7 +129,7 @@ export const languageUIMessage = (
|
||||
|
||||
|
||||
export const untranslatedKeys = (
|
||||
locale: LocaleCode,
|
||||
locale: UILocaleCode,
|
||||
namespace = 'navigation',
|
||||
): string[] => {
|
||||
const collect = (resource: Record<string, unknown>, prefix = ''): string[] =>
|
||||
|
||||
@@ -7,6 +7,10 @@ export const SUPPORTED_LOCALES = ['ja', 'ain-Kana'] as const
|
||||
|
||||
export type LocaleCode = typeof SUPPORTED_LOCALES[number]
|
||||
|
||||
// This UI locale uses Japanese content until the API supports Mongolian.
|
||||
export const MONGOLIAN_LOCALE = { code: 'mn-Mong', name: 'ᠮᠣᠩᠭᠣᠯ' } as const
|
||||
export type UILocaleCode = LocaleCode | typeof MONGOLIAN_LOCALE.code
|
||||
|
||||
export type AvailableLocale = { code: string; name: string }
|
||||
|
||||
|
||||
@@ -28,9 +32,17 @@ export const localeFromPath = (pathname: string): LocaleCode | null => {
|
||||
}
|
||||
|
||||
|
||||
// A development-only layout route; API and dictionary locales stay unchanged.
|
||||
// Frontend layout selection must not change the API's content locale.
|
||||
export const isVerticalPreviewPath = (pathname: string): boolean =>
|
||||
import.meta.env.DEV && /^\/mn-Mong(?:[/?#]|$)/.test (pathname)
|
||||
/^\/mn-Mong(?:[/?#]|$)/.test (pathname)
|
||||
|
||||
|
||||
export const isUILocaleCode = (value: unknown): value is UILocaleCode =>
|
||||
value === MONGOLIAN_LOCALE.code || isLocaleCode (value)
|
||||
|
||||
|
||||
export const uiLocaleFromPath = (pathname: string): UILocaleCode =>
|
||||
isVerticalPreviewPath (pathname) ? MONGOLIAN_LOCALE.code : localeFromPath (pathname) ?? 'ja'
|
||||
|
||||
|
||||
export const resolveViewingLocale = (
|
||||
@@ -67,7 +79,7 @@ export const localiseTo = (to: To, locale: LocaleCode, viewingPath = ''): To =>
|
||||
|
||||
export const switchLocalePath = (
|
||||
location: { pathname: string; search: string; hash: string },
|
||||
locale: LocaleCode,
|
||||
locale: UILocaleCode,
|
||||
): string =>
|
||||
`/${ locale }${ unlocalisedPath (location.pathname) }${ location.search }${ location.hash }`
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { translateUIMessage } from '@/lib/i18n'
|
||||
|
||||
import type navigation from '@/locales/ja/navigation.json'
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { UILocaleCode } from '@/lib/locales'
|
||||
|
||||
export type UIMessageKey = keyof typeof navigation
|
||||
|
||||
|
||||
export const uiMessage = (
|
||||
locale: LocaleCode,
|
||||
locale: UILocaleCode,
|
||||
key: UIMessageKey,
|
||||
values: Record<string, string | number> = { },
|
||||
): string => translateUIMessage (key, 'navigation', locale, values).text
|
||||
|
||||
|
||||
export const uiMessageLanguage = (locale: LocaleCode, key: UIMessageKey): 'ja' | 'ain' =>
|
||||
export const uiMessageLanguage = (locale: UILocaleCode, key: UIMessageKey): 'ja' | 'ain' =>
|
||||
translateUIMessage (key, 'navigation', locale).language === 'ain-Kana'
|
||||
? 'ain'
|
||||
: 'ja'
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
|
||||
import { isVerticalPreviewPath, localiseTo, resolveViewingLocale } from '@/lib/locales'
|
||||
import { isVerticalPreviewPath, localiseTo,
|
||||
resolveViewingLocale, uiLocaleFromPath } from '@/lib/locales'
|
||||
|
||||
import type { NavigateFunction, NavigateOptions, To } from 'react-router-dom'
|
||||
|
||||
import type { LocaleCode } from '@/lib/locales'
|
||||
import type { LocaleCode, UILocaleCode } from '@/lib/locales'
|
||||
|
||||
|
||||
export const useLocale = (): LocaleCode => {
|
||||
@@ -15,6 +16,10 @@ export const useLocale = (): LocaleCode => {
|
||||
}
|
||||
|
||||
|
||||
export const useUILocale = (): UILocaleCode =>
|
||||
uiLocaleFromPath (useLocation ().pathname)
|
||||
|
||||
|
||||
export const useLocaleNavigate = (): NavigateFunction => {
|
||||
const navigate = useNavigate ()
|
||||
const locale = useLocale ()
|
||||
|
||||
@@ -3,11 +3,11 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import LocalisedText from '@/components/LocalisedText'
|
||||
import { translateUIMessage } from '@/lib/i18n'
|
||||
import { useLocale } from '@/lib/useLocale'
|
||||
import { useUILocale } from '@/lib/useLocale'
|
||||
|
||||
|
||||
export const useUITranslation = (namespace = 'common') => {
|
||||
const locale = useLocale ()
|
||||
const locale = useUILocale ()
|
||||
const { i18n } = useTranslation (namespace, { lng: locale })
|
||||
|
||||
const message = useCallback ((key: string, values?: Record<string, string | number>) => {
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { I18nextProvider } from 'react-i18next'
|
||||
import '@/index.css'
|
||||
import App, { preloadPageRoute, resolvePageRoute } from '@/App'
|
||||
import { createUII18n } from '@/lib/i18n'
|
||||
import { currentViewingLocale } from '@/lib/locales'
|
||||
import { currentViewingLocale, uiLocaleFromPath } from '@/lib/locales'
|
||||
import { localesKeys } from '@/lib/queryKeys'
|
||||
import { applyClientAnimationMode, getClientAnimationMode } from '@/lib/settings'
|
||||
|
||||
@@ -22,7 +22,7 @@ const client = new QueryClient ({
|
||||
|
||||
|
||||
const mount = (serverRendered: boolean) => {
|
||||
const instance = createUII18n (currentViewingLocale ())
|
||||
const instance = createUII18n (uiLocaleFromPath (window.location.pathname))
|
||||
const app = (
|
||||
<HelmetProvider context={helmetContext}>
|
||||
<QueryClientProvider client={client}>
|
||||
|
||||
@@ -359,7 +359,8 @@ const CountsResourceContent = (
|
||||
</ul>
|
||||
</nav>
|
||||
</SidebarComponent>
|
||||
<MainArea animateLayout={false} className="flex min-h-0 min-w-0 flex-col p-2">
|
||||
<MainArea animateLayout={false} verticalContent={false}
|
||||
className="flex min-h-0 min-w-0 flex-col p-2">
|
||||
<PageTitle className="mb-1 text-base" lang={title.language}>{title.text}</PageTitle>
|
||||
<div ref={controlsArea} className="shrink-0"
|
||||
style={{ minHeight: definition == null ? controlsHeight : undefined }}>
|
||||
|
||||
@@ -107,7 +107,7 @@ const PostDetailPage: FC<Props> = ({ user }) => {
|
||||
|
||||
{post && <TagDetailSidebar post={post} className="hidden md:block"/>}
|
||||
|
||||
<MainArea className="relative">
|
||||
<MainArea className="relative" verticalContent={false}>
|
||||
{post
|
||||
? (
|
||||
<>
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする