| | import type { UIStrings } from '@/frame/components/context/MainContext' |
| |
|
| | class UngettableError extends Error {} |
| |
|
| | |
| | |
| | |
| | |
| | export function createTranslationFunctions(uiData: UIStrings, namespaces: string | Array<string>) { |
| | const namespacesArray = Array.isArray(namespaces) ? namespaces : [namespaces] |
| |
|
| | const missingNamespaces: string[] = [] |
| | for (const namespace of namespacesArray) { |
| | if (!(namespace in uiData)) { |
| | missingNamespaces.push(namespace) |
| | } |
| | } |
| |
|
| | if (missingNamespaces.length > 0) { |
| | console.warn( |
| | `Missing namespaces [${missingNamespaces.join(', ')}] in UI data. ` + |
| | `Available namespaces: ${Object.keys(uiData).sort().join(', ')}`, |
| | ) |
| |
|
| | |
| | if (missingNamespaces.includes('meta')) { |
| | console.warn('Creating fallback meta namespace for 404 page rendering') |
| | uiData = { |
| | ...uiData, |
| | meta: { |
| | oops: 'Ooops!', |
| | ...(typeof uiData.meta === 'object' && uiData.meta !== null ? uiData.meta : {}), |
| | }, |
| | } as UIStrings |
| | } |
| |
|
| | |
| | for (const namespace of missingNamespaces) { |
| | if (!(namespace in uiData)) { |
| | uiData = { |
| | ...uiData, |
| | [namespace]: {} as UIStrings, |
| | } as UIStrings |
| | } |
| | } |
| | } |
| |
|
| | function carefulGetWrapper(path: string, fallback?: string) { |
| | try { |
| | |
| | for (const namespace of namespacesArray) { |
| | if (!(namespace in uiData)) { |
| | continue |
| | } |
| | const deeper = uiData[namespace] |
| | if (typeof deeper === 'string') { |
| | continue |
| | } |
| | try { |
| | return carefulGet(deeper, path) |
| | } catch (error) { |
| | if (!(error instanceof UngettableError)) { |
| | console.warn(`Translation error in namespace "${namespace}" for path "${path}":`, error) |
| | } |
| | } |
| | } |
| |
|
| | |
| | return carefulGet(uiData, path) |
| | } catch { |
| | |
| | const finalFallback = fallback || path.split('.').pop() || 'Missing translation' |
| | console.warn( |
| | `Translation completely failed for "${path}", using fallback: "${finalFallback}"`, |
| | ) |
| | return finalFallback |
| | } |
| | } |
| |
|
| | return { |
| | tObject: (strings: TemplateStringsArray | string) => { |
| | const key = typeof strings === 'string' ? strings : String.raw(strings) |
| | try { |
| | return carefulGetWrapper(key) as UIStrings |
| | } catch (error) { |
| | console.warn(`tObject failed for "${key}":`, error) |
| | return {} as UIStrings |
| | } |
| | }, |
| | t: (strings: TemplateStringsArray | string, ...values: Array<any>) => { |
| | const key = typeof strings === 'string' ? strings : String.raw(strings, ...values) |
| | |
| | const commonFallbacks: Record<string, string> = { |
| | oops: 'Ooops!', |
| | github_docs: 'GitHub Docs', |
| | } |
| | const fallback = commonFallbacks[key] || commonFallbacks[key.split('.').pop() || ''] |
| | return carefulGetWrapper(key, fallback) as string |
| | }, |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | export function translate(uiData: UIStrings, key: string, fallback?: string): string { |
| | |
| | if (!uiData || typeof uiData !== 'object') { |
| | console.warn(`UI data is missing or corrupted for key "${key}", using fallback`) |
| | return getCommonFallback(key, fallback) |
| | } |
| |
|
| | try { |
| | return carefulGet(uiData, key) as string |
| | } catch (error) { |
| | const finalFallback = getCommonFallback(key, fallback) |
| |
|
| | |
| | if (process.env.NODE_ENV === 'development') { |
| | console.warn( |
| | `Server translation failed for "${key}":`, |
| | error instanceof Error ? error.message : error, |
| | `Using fallback: "${finalFallback}"`, |
| | ) |
| | } |
| |
|
| | return finalFallback |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | function getCommonFallback(key: string, providedFallback?: string): string { |
| | const commonFallbacks: Record<string, string> = { |
| | 'meta.oops': 'Ooops!', |
| | 'header.github_docs': 'GitHub Docs', |
| | 'meta.default_description': 'Get started, troubleshoot, and make the most of GitHub.', |
| | 'footer.terms': 'Terms', |
| | 'footer.privacy': 'Privacy', |
| | 'footer.status': 'Status', |
| | 'support.contact_support': 'Contact support', |
| | } |
| |
|
| | return ( |
| | commonFallbacks[key] || |
| | providedFallback || |
| | commonFallbacks[key.split('.').pop() || ''] || |
| | key.split('.').pop() || |
| | key |
| | ) |
| | } |
| |
|
| | function carefulGet(uiData: UIStrings, dottedPath: string) { |
| | const splitPath = dottedPath.split('.') |
| | const start = splitPath[0] |
| | if (!(start in uiData)) { |
| | throw new UngettableError( |
| | `Namespace "${start}" not found in loaded data (available: ${Object.keys(uiData).sort()})`, |
| | ) |
| | } |
| | if (splitPath.length > 1) { |
| | const deeper = uiData[start] |
| | if (typeof deeper === 'string') { |
| | throw new Error(`Namespace "${start}" is a string, not an object`) |
| | } |
| | return carefulGet(deeper, splitPath.slice(1).join('.')) |
| | } else { |
| | if (!(start in uiData)) { |
| | throw new UngettableError(`Key "${start}" not found in loaded data`) |
| | } |
| | return uiData[start] |
| | } |
| | } |
| |
|