87 行
3.0 KiB
TypeScript
87 行
3.0 KiB
TypeScript
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 ()
|
|
|
|
|
|
// Keep Japanese placeholders editable without reporting them as completed translations.
|
|
const translatedResource = (resource: Record<string, unknown>): Record<string, unknown> => {
|
|
const untranslated = new Set (
|
|
Array.isArray (resource._untranslated) ? resource._untranslated : [])
|
|
const select = (entries: Record<string, unknown>, prefix = ''): Record<string, unknown> =>
|
|
Object.fromEntries (Object.entries (entries).flatMap (([key, value]) => {
|
|
const path = prefix ? `${ prefix }.${ key }` : key
|
|
if (key === '_untranslated' || untranslated.has (path))
|
|
return []
|
|
const translated =
|
|
(value != null && typeof value === 'object' && !(Array.isArray (value)))
|
|
? select (value as Record<string, unknown>, path)
|
|
: value
|
|
return [[key, translated]]
|
|
}))
|
|
return select (resource)
|
|
}
|
|
|
|
|
|
void i18n.use (initReactI18next).init ({
|
|
lng: 'ja',
|
|
fallbackLng: 'ja',
|
|
supportedLngs: ['ja', 'ain-Kana'],
|
|
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) } },
|
|
interpolation: { escapeValue: false },
|
|
returnNull: false,
|
|
returnEmptyString: false,
|
|
saveMissing: import.meta.env.DEV,
|
|
missingKeyHandler: (languages, namespace, key) => {
|
|
console.warn ('[i18n] missing key', { languages, namespace, key })
|
|
},
|
|
react: { useSuspense: false } })
|
|
|
|
|
|
export const untranslatedKeys = (
|
|
locale: LocaleCode,
|
|
namespace = 'navigation',
|
|
): string[] => {
|
|
const collect = (resource: Record<string, unknown>, prefix = ''): string[] =>
|
|
Object.entries (resource).flatMap (([key, value]) => {
|
|
const path = prefix ? `${ prefix }.${ key }` : key
|
|
return (value != null && typeof value === 'object')
|
|
? collect (value as Record<string, unknown>, path)
|
|
: [path]
|
|
})
|
|
const baseline = i18n.getResourceBundle ('ja', namespace) ?? { }
|
|
return collect (baseline).filter (key => {
|
|
const value = i18n.getResource (locale, namespace, key)
|
|
return typeof value !== 'string' || value.length === 0
|
|
})
|
|
}
|
|
|
|
|
|
if (import.meta.env.DEV)
|
|
{
|
|
for (const namespace of ['navigation', 'common', 'tags'])
|
|
{
|
|
const missing = untranslatedKeys ('ain-Kana', namespace)
|
|
if (missing.length > 0)
|
|
console.warn (`[i18n] ain-Kana: untranslated ${ namespace } keys`, missing)
|
|
}
|
|
}
|