1
0
mirror of synced 2025-12-19 18:10:59 -05:00

Refactor languages module: separate client and server code (#57949)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Kevin Heis
2025-10-23 12:30:06 -07:00
committed by GitHub
parent f4ea74a1f6
commit 841f6b1718
63 changed files with 230 additions and 297 deletions

View File

@@ -4,14 +4,9 @@ import type { NextConfig } from 'next'
import frontmatter from '@gr2m/gray-matter' import frontmatter from '@gr2m/gray-matter'
import { getLogLevelNumber } from '@/observability/logger/lib/log-levels' import { getLogLevelNumber } from '@/observability/logger/lib/log-levels'
import { languageKeys } from '@/languages/lib/languages'
const ROOT = process.env.ROOT || '.' const ROOT = process.env.ROOT || '.'
// Language keys are defined here because Next.js config compilation doesn't resolve the @/ path alias
// Importing from src/languages/lib/languages.ts would fail when it tries to import @/frame/lib/constants
// This must match the languages defined in src/languages/lib/languages.ts
const languageKeys = ['en', 'es', 'ja', 'pt', 'zh', 'ru', 'fr', 'ko', 'de']
const homepage = path.posix.join(ROOT, 'content/index.md') const homepage = path.posix.join(ROOT, 'content/index.md')
const { data } = frontmatter(fs.readFileSync(homepage, 'utf8')) const { data } = frontmatter(fs.readFileSync(homepage, 'utf8'))
const productIds = data.children as string[] const productIds = data.children as string[]

View File

@@ -10,7 +10,7 @@ import { initializeEvents } from '@/events/components/events'
import { CTAPopoverProvider } from '@/frame/components/context/CTAContext' import { CTAPopoverProvider } from '@/frame/components/context/CTAContext'
import { SharedUIContextProvider } from '@/frame/components/context/SharedUIContext' import { SharedUIContextProvider } from '@/frame/components/context/SharedUIContext'
import { LanguagesContext, LanguagesContextT } from '@/languages/components/LanguagesContext' import { LanguagesContext, LanguagesContextT } from '@/languages/components/LanguagesContext'
import { clientLanguages, type ClientLanguageCode } from '@/languages/lib/client-languages' import { languages, type LanguageCode } from '@/languages/lib/languages'
import { MainContextProvider } from '@/app/components/MainContextProvider' import { MainContextProvider } from '@/app/components/MainContextProvider'
import { createMinimalMainContext } from '@/app/lib/main-context-adapter' import { createMinimalMainContext } from '@/app/lib/main-context-adapter'
import type { AppRouterContext } from '@/app/lib/app-router-context' import type { AppRouterContext } from '@/app/lib/app-router-context'
@@ -31,16 +31,11 @@ interface ClientLayoutProps {
export function ClientLayout({ children, appContext, pageData }: ClientLayoutProps): JSX.Element { export function ClientLayout({ children, appContext, pageData }: ClientLayoutProps): JSX.Element {
const { theme } = useTheme() const { theme } = useTheme()
const locale: ClientLanguageCode = useDetectLocale() const locale: LanguageCode = useDetectLocale()
const [isInitialized, setIsInitialized] = useState(false) const [isInitialized, setIsInitialized] = useState(false)
const [initializationError, setInitializationError] = useState<Error | null>(null) const [initializationError, setInitializationError] = useState<Error | null>(null)
const languagesContext: LanguagesContextT = useMemo( const languagesContext: LanguagesContextT = useMemo(() => ({ languages }), [])
() => ({
languages: clientLanguages,
}),
[],
)
// Create MainContext-compatible data for App Router // Create MainContext-compatible data for App Router
const mainContext = useMemo( const mainContext = useMemo(

View File

@@ -1,7 +1,7 @@
'use client' 'use client'
import { createContext, useContext } from 'react' import { createContext, useContext } from 'react'
import { clientLanguages, type ClientLanguageCode } from '@/languages/lib/client-languages' import { languages, type LanguageCode } from '@/languages/lib/languages'
export type AppRouterLanguageItem = { export type AppRouterLanguageItem = {
name: string name: string
@@ -12,7 +12,7 @@ export type AppRouterLanguageItem = {
export type AppRouterLanguagesContextT = { export type AppRouterLanguagesContextT = {
languages: Record<string, AppRouterLanguageItem> languages: Record<string, AppRouterLanguageItem>
currentLanguage?: ClientLanguageCode currentLanguage?: LanguageCode
} }
export const AppRouterLanguagesContext = createContext<AppRouterLanguagesContextT | null>(null) export const AppRouterLanguagesContext = createContext<AppRouterLanguagesContextT | null>(null)
@@ -34,7 +34,7 @@ export const useAppRouterLanguages = (): AppRouterLanguagesContextT => {
*/ */
interface AppRouterLanguagesProviderProps { interface AppRouterLanguagesProviderProps {
children: React.ReactNode children: React.ReactNode
currentLanguage?: ClientLanguageCode currentLanguage?: LanguageCode
} }
export function AppRouterLanguagesProvider({ export function AppRouterLanguagesProvider({
@@ -42,7 +42,7 @@ export function AppRouterLanguagesProvider({
currentLanguage, currentLanguage,
}: AppRouterLanguagesProviderProps) { }: AppRouterLanguagesProviderProps) {
const value: AppRouterLanguagesContextT = { const value: AppRouterLanguagesContextT = {
languages: clientLanguages, languages,
currentLanguage, currentLanguage,
} }

View File

@@ -1,10 +1,10 @@
import { getUIDataMerged } from '@/data-directory/lib/get-data' import { getUIDataMerged } from '@/data-directory/lib/get-data'
import { createTranslationFunctions } from '@/languages/lib/translation-utils' import { createTranslationFunctions } from '@/languages/lib/translation-utils'
import { LinkExternalIcon } from '@primer/octicons-react' import { LinkExternalIcon } from '@primer/octicons-react'
import type { ClientLanguageCode } from '@/languages/lib/client-languages' import type { LanguageCode } from '@/languages/lib/languages'
interface ServerFooterProps { interface ServerFooterProps {
currentLanguage: ClientLanguageCode currentLanguage: LanguageCode
} }
export function ServerFooter({ currentLanguage }: ServerFooterProps) { export function ServerFooter({ currentLanguage }: ServerFooterProps) {

View File

@@ -1,10 +1,10 @@
import { getUIDataMerged } from '@/data-directory/lib/get-data' import { getUIDataMerged } from '@/data-directory/lib/get-data'
import { type ClientLanguageCode } from '@/languages/lib/client-languages' import { type LanguageCode } from '@/languages/lib/languages'
import { translate } from '@/languages/lib/translation-utils' import { translate } from '@/languages/lib/translation-utils'
import { extractLanguageFromPath } from '@/app/lib/language-utils' import { extractLanguageFromPath } from '@/app/lib/language-utils'
export interface AppRouterContext { export interface AppRouterContext {
currentLanguage: ClientLanguageCode currentLanguage: LanguageCode
currentVersion: string currentVersion: string
sitename: string sitename: string
site: { site: {
@@ -19,7 +19,7 @@ export interface AppRouterContext {
*/ */
export function createAppRouterContext( export function createAppRouterContext(
pathname: string = '/', pathname: string = '/',
fallbackLanguage?: ClientLanguageCode, fallbackLanguage?: LanguageCode,
): AppRouterContext { ): AppRouterContext {
let language = extractLanguageFromPath(pathname) let language = extractLanguageFromPath(pathname)

View File

@@ -1,16 +1,16 @@
import { clientLanguageKeys, type ClientLanguageCode } from '@/languages/lib/client-languages' import { languageKeys, type LanguageCode } from '@/languages/lib/languages'
/** /**
* Extract language from URL path * Extract language from URL path
* Handles paths like /en/something, /es/articles, etc. * Handles paths like /en/something, /es/articles, etc.
*/ */
export function extractLanguageFromPath(path: string): ClientLanguageCode { export function extractLanguageFromPath(path: string): LanguageCode {
try { try {
const pathSegments = path.split('/') const pathSegments = path.split('/')
const firstSegment = pathSegments[1] const firstSegment = pathSegments[1]
if (firstSegment && clientLanguageKeys.includes(firstSegment)) { if (firstSegment && languageKeys.includes(firstSegment)) {
return firstSegment as ClientLanguageCode return firstSegment as LanguageCode
} }
} catch (error) { } catch (error) {
console.warn('Failed to extract language from path:', error) console.warn('Failed to extract language from path:', error)
@@ -24,7 +24,7 @@ export function extractLanguageFromPath(path: string): ClientLanguageCode {
export function hasLanguagePrefix(path: string): boolean { export function hasLanguagePrefix(path: string): boolean {
const pathSegments = path.split('/') const pathSegments = path.split('/')
const firstSegment = pathSegments[1] const firstSegment = pathSegments[1]
return Boolean(firstSegment && clientLanguageKeys.includes(firstSegment)) return Boolean(firstSegment && languageKeys.includes(firstSegment))
} }
/** /**
@@ -43,7 +43,7 @@ export function stripLanguagePrefix(path: string): string {
* Add language prefix to path if it doesn't have one * Add language prefix to path if it doesn't have one
* e.g., /articles/example + 'es' -> /es/articles/example * e.g., /articles/example + 'es' -> /es/articles/example
*/ */
export function addLanguagePrefix(path: string, language: ClientLanguageCode): string { export function addLanguagePrefix(path: string, language: LanguageCode): string {
if (hasLanguagePrefix(path)) { if (hasLanguagePrefix(path)) {
return path return path
} }

View File

@@ -1,55 +1,51 @@
'use client' 'use client'
import { createContext, useContext, ReactNode, useMemo } from 'react' import { createContext, useContext, ReactNode, useMemo } from 'react'
import { import { languages, languageKeys, type LanguageCode } from '@/languages/lib/languages'
clientLanguages,
clientLanguageKeys,
type ClientLanguageCode,
} from '@/languages/lib/client-languages'
interface LocaleContextType { interface LocaleContextType {
readonly locale: ClientLanguageCode readonly locale: LanguageCode
readonly isValidLocale: (locale: string) => locale is ClientLanguageCode readonly isValidLocale: (locale: string) => locale is LanguageCode
readonly getSupportedLocales: () => readonly ClientLanguageCode[] readonly getSupportedLocales: () => readonly LanguageCode[]
readonly getLocaleDisplayName: (locale: ClientLanguageCode) => string readonly getLocaleDisplayName: (locale: LanguageCode) => string
readonly getLocaleNativeName: (locale: ClientLanguageCode) => string readonly getLocaleNativeName: (locale: LanguageCode) => string
} }
const LocaleContext = createContext<LocaleContextType | null>(null) const LocaleContext = createContext<LocaleContextType | null>(null)
interface LocaleProviderProps { interface LocaleProviderProps {
readonly children: ReactNode readonly children: ReactNode
readonly locale: ClientLanguageCode readonly locale: LanguageCode
} }
// Use client languages as the source of truth for supported locales // Use client languages as the source of truth for supported locales
const SUPPORTED_LOCALES: readonly ClientLanguageCode[] = clientLanguageKeys as ClientLanguageCode[] const SUPPORTED_LOCALES: readonly LanguageCode[] = languageKeys as LanguageCode[]
/** /**
* Validates if a string is a supported locale * Validates if a string is a supported locale
*/ */
function isValidLocale(locale: string): locale is ClientLanguageCode { function isValidLocale(locale: string): locale is LanguageCode {
return clientLanguageKeys.includes(locale) return languageKeys.includes(locale)
} }
/** /**
* Gets display name for a locale from client languages data * Gets display name for a locale from languages module
*/ */
function getLocaleDisplayName(locale: ClientLanguageCode): string { function getLocaleDisplayName(locale: LanguageCode): string {
return clientLanguages[locale]?.name || locale return languages[locale]?.name || locale
} }
/** /**
* Gets native name for a locale from client languages data * Gets native name for a locale from languages module
*/ */
function getLocaleNativeName(locale: ClientLanguageCode): string { function getLocaleNativeName(locale: LanguageCode): string {
return clientLanguages[locale]?.nativeName || clientLanguages[locale]?.name || locale return languages[locale]?.nativeName || languages[locale]?.name || locale
} }
/** /**
* Gets browser language preference as a valid locale * Gets browser language preference as a valid locale
*/ */
function getBrowserLocale(): ClientLanguageCode { function getBrowserLocale(): LanguageCode {
if (typeof window === 'undefined') return 'en' if (typeof window === 'undefined') return 'en'
const browserLang = window.navigator.language.split('-')[0] const browserLang = window.navigator.language.split('-')[0]
@@ -77,7 +73,7 @@ export function LocaleProvider({ children, locale }: LocaleProviderProps): JSX.E
/** /**
* Hook to get current locale with enhanced error handling * Hook to get current locale with enhanced error handling
*/ */
export function useLocale(): ClientLanguageCode { export function useLocale(): LanguageCode {
const context = useContext(LocaleContext) const context = useContext(LocaleContext)
if (context) { if (context) {
@@ -118,4 +114,4 @@ export function useLocaleContext(): LocaleContextType {
} }
export { isValidLocale, getLocaleDisplayName, getLocaleNativeName } export { isValidLocale, getLocaleDisplayName, getLocaleNativeName }
export type { LocaleContextType, ClientLanguageCode } export type { LocaleContextType, LanguageCode }

View File

@@ -1,11 +1,11 @@
import { extractLanguageFromPath } from '@/app/lib/language-utils' import { extractLanguageFromPath } from '@/app/lib/language-utils'
import { extractVersionFromPath } from '@/app/lib/version-utils' import { extractVersionFromPath } from '@/app/lib/version-utils'
import { getUIDataMerged } from '@/data-directory/lib/get-data' import { getUIDataMerged } from '@/data-directory/lib/get-data'
import { type ClientLanguageCode } from '@/languages/lib/client-languages' import { type LanguageCode } from '@/languages/lib/languages'
import { createTranslationFunctions, translate } from '@/languages/lib/translation-utils' import { createTranslationFunctions, translate } from '@/languages/lib/translation-utils'
export interface ServerAppRouterContext { export interface ServerAppRouterContext {
currentLanguage: ClientLanguageCode currentLanguage: LanguageCode
currentVersion: string currentVersion: string
sitename: string sitename: string
site: { data: { ui: any } } site: { data: { ui: any } }
@@ -33,7 +33,7 @@ export function createServerAppRouterContext(pathname: string): ServerAppRouterC
/** /**
* Create server-side footer with translations * Create server-side footer with translations
*/ */
export function createServerFooterContent(language: ClientLanguageCode) { export function createServerFooterContent(language: LanguageCode) {
const uiData = getUIDataMerged(language) const uiData = getUIDataMerged(language)
const { t } = createTranslationFunctions(uiData, 'footer') const { t } = createTranslationFunctions(uiData, 'footer')

View File

@@ -2,24 +2,24 @@
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'
import { useMemo, useEffect, useState } from 'react' import { useMemo, useEffect, useState } from 'react'
import { clientLanguageKeys, type ClientLanguageCode } from '@/languages/lib/client-languages' import { languageKeys, type LanguageCode } from '@/languages/lib/languages'
import Cookies from '@/frame/components/lib/cookies' import Cookies from '@/frame/components/lib/cookies'
import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants' import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants'
/** /**
* Validates if a string is a supported locale using client languages * Validates if a string is a supported locale using client languages
*/ */
function isValidLocale(locale: string): locale is ClientLanguageCode { function isValidLocale(locale: string): locale is LanguageCode {
return clientLanguageKeys.includes(locale) return languageKeys.includes(locale)
} }
/** /**
* Hook to detect locale from various sources with fallback logic * Hook to detect locale from various sources with fallback logic
*/ */
export function useDetectLocale(): ClientLanguageCode { export function useDetectLocale(): LanguageCode {
const pathname = usePathname() const pathname = usePathname()
const [cookieLanguage, setCookieLanguage] = useState<ClientLanguageCode | null>(null) const [cookieLanguage, setCookieLanguage] = useState<LanguageCode | null>(null)
const [browserLanguage, setBrowserLanguage] = useState<ClientLanguageCode | null>(null) const [browserLanguage, setBrowserLanguage] = useState<LanguageCode | null>(null)
// Read cookie and browser language on client-side mount // Read cookie and browser language on client-side mount
useEffect(() => { useEffect(() => {
@@ -71,7 +71,7 @@ export function useDetectLocale(): ClientLanguageCode {
/** /**
* Utility function to detect locale from pathname (for server-side use) * Utility function to detect locale from pathname (for server-side use)
*/ */
export function detectLocaleFromPath(pathname: string): ClientLanguageCode { export function detectLocaleFromPath(pathname: string): LanguageCode {
const pathSegments = pathname.split('/') const pathSegments = pathname.split('/')
const firstSegment = pathSegments[1] const firstSegment = pathSegments[1]
@@ -82,8 +82,8 @@ export function detectLocaleFromPath(pathname: string): ClientLanguageCode {
return 'en' return 'en'
} }
export function getSupportedLocales(): readonly ClientLanguageCode[] { export function getSupportedLocales(): readonly LanguageCode[] {
return clientLanguageKeys as ClientLanguageCode[] return languageKeys as LanguageCode[]
} }
export { isValidLocale } export { isValidLocale }

View File

@@ -2,14 +2,14 @@
* Enhanced type definitions for the app router with strict validation * Enhanced type definitions for the app router with strict validation
*/ */
import type { ClientLanguageCode } from '@/languages/lib/client-languages' import type { LanguageCode } from '@/languages/lib/languages'
// Core theme types with strict validation // Core theme types with strict validation
export type Theme = 'light' | 'dark' | 'auto' export type Theme = 'light' | 'dark' | 'auto'
export type ColorMode = 'light' | 'dark' export type ColorMode = 'light' | 'dark'
// Re-export locale type from client-languages for consistency // Re-export locale type from languages.ts for consistency
export type Locale = ClientLanguageCode export type Locale = LanguageCode
// Version and product identifiers with validation // Version and product identifiers with validation
export type VersionId = string export type VersionId = string

View File

@@ -14,7 +14,7 @@ import { isArchivedVersion } from '@/archives/lib/is-archived-version'
import { setFastlySurrogateKey, SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key' import { setFastlySurrogateKey, SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
import { readCompressedJsonFileFallbackLazily } from '@/frame/lib/read-json-file' import { readCompressedJsonFileFallbackLazily } from '@/frame/lib/read-json-file'
import { archivedCacheControl, languageCacheControl } from '@/frame/middleware/cache-control' import { archivedCacheControl, languageCacheControl } from '@/frame/middleware/cache-control'
import { pathLanguagePrefixed, languagePrefixPathRegex } from '@/languages/lib/languages' import { pathLanguagePrefixed, languagePrefixPathRegex } from '@/languages/lib/languages-server'
import getRedirect, { splitPathByLanguage } from '@/redirects/lib/get-redirect' import getRedirect, { splitPathByLanguage } from '@/redirects/lib/get-redirect'
import getRemoteJSON from '@/frame/lib/get-remote-json' import getRemoteJSON from '@/frame/lib/get-remote-json'
import { ExtendedRequest } from '@/types' import { ExtendedRequest } from '@/types'

View File

@@ -30,7 +30,7 @@ import { brotliCompressSync } from 'zlib'
import chalk from 'chalk' import chalk from 'chalk'
import { program, Option } from 'commander' import { program, Option } from 'commander'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { loadPages, loadUnversionedTree } from '@/frame/lib/page-data' import { loadPages, loadUnversionedTree } from '@/frame/lib/page-data'
import { CACHE_FILE_PATH, getPageInfo } from '../middleware/article-pageinfo' import { CACHE_FILE_PATH, getPageInfo } from '../middleware/article-pageinfo'

View File

@@ -11,7 +11,7 @@ import { program } from 'commander'
import walk from 'walk-sync' import walk from 'walk-sync'
import walkFiles from '@/workflows/walk-files' import walkFiles from '@/workflows/walk-files'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
const EXCEPTIONS = new Set([ const EXCEPTIONS = new Set([
'assets/images/site/favicon.ico', 'assets/images/site/favicon.ico',

View File

@@ -7,7 +7,7 @@ import { visitParents } from 'unist-util-visit-parents'
import { visit, SKIP } from 'unist-util-visit' import { visit, SKIP } from 'unist-util-visit'
import { remove } from 'unist-util-remove' import { remove } from 'unist-util-remove'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { MARKDOWN_OPTIONS } from '../../content-linter/lib/helpers/unified-formatter-options' import { MARKDOWN_OPTIONS } from '../../content-linter/lib/helpers/unified-formatter-options'
interface Config { interface Config {

View File

@@ -2,7 +2,7 @@
import { filterTokens } from 'markdownlint-rule-helpers' import { filterTokens } from 'markdownlint-rule-helpers'
import { addFixErrorDetail, getRange } from '../helpers/utils' import { addFixErrorDetail, getRange } from '../helpers/utils'
import { allLanguageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages'
import type { RuleParams, RuleErrorCallback, Rule } from '../../types' import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
export const internalLinksNoLang: Rule = { export const internalLinksNoLang: Rule = {
@@ -28,7 +28,7 @@ export const internalLinksNoLang: Rule = {
.filter((attr: [string, string]) => attr[1].startsWith('/') || !attr[1].startsWith('//')) .filter((attr: [string, string]) => attr[1].startsWith('/') || !attr[1].startsWith('//'))
// Filter out link paths that start with language code // Filter out link paths that start with language code
.filter((attr: [string, string]) => .filter((attr: [string, string]) =>
allLanguageKeys.some((lang) => attr[1].split('/')[1] === lang), languageKeys.some((lang) => attr[1].split('/')[1] === lang),
) )
// Get the link path from the attribute // Get the link path from the attribute
.map((attr: [string, string]) => attr[1]) .map((attr: [string, string]) => attr[1])

View File

@@ -15,7 +15,7 @@ import { defaultConfig } from '../lib/default-markdownlint-options'
import { prettyPrintResults } from './pretty-print-results' import { prettyPrintResults } from './pretty-print-results'
import { getLintableYml } from '@/content-linter/lib/helpers/get-lintable-yml' import { getLintableYml } from '@/content-linter/lib/helpers/get-lintable-yml'
import { printAnnotationResults } from '../lib/helpers/print-annotations' import { printAnnotationResults } from '../lib/helpers/print-annotations'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { shouldIncludeResult } from '../lib/helpers/should-include-result' import { shouldIncludeResult } from '../lib/helpers/should-include-result'
program program

View File

@@ -9,7 +9,7 @@ import walk from 'walk-sync'
import { zip } from 'lodash-es' import { zip } from 'lodash-es'
import { beforeAll, describe, expect, test } from 'vitest' import { beforeAll, describe, expect, test } from 'vitest'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { getDiffFiles } from '../lib/diff-files' import { getDiffFiles } from '../lib/diff-files'
const __dirname = path.dirname(fileURLToPath(import.meta.url)) const __dirname = path.dirname(fileURLToPath(import.meta.url))

View File

@@ -43,7 +43,7 @@ import { writeFileSync, statSync } from 'fs'
import { program, Option } from 'commander' import { program, Option } from 'commander'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
import { allDocuments, POSSIBLE_FIELDS, type AllDocument } from './lib' import { allDocuments, POSSIBLE_FIELDS, type AllDocument } from './lib'

View File

@@ -1,7 +1,7 @@
import { afterAll, beforeAll, describe, expect, test } from 'vitest' import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import Page from '@/frame/lib/page' import Page from '@/frame/lib/page'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version' import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
import { DataDirectory } from '@/tests/helpers/data-directory' import { DataDirectory } from '@/tests/helpers/data-directory'

View File

@@ -1,7 +1,7 @@
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest' import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'
import { liquid } from '@/content-render/index' import { liquid } from '@/content-render/index'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { DataDirectory } from '@/tests/helpers/data-directory' import { DataDirectory } from '@/tests/helpers/data-directory'
describe('liquid helper tags', () => { describe('liquid helper tags', () => {

View File

@@ -5,7 +5,7 @@ import yaml from 'js-yaml'
import matter from '@gr2m/gray-matter' import matter from '@gr2m/gray-matter'
import { merge, get } from 'lodash-es' import { merge, get } from 'lodash-es'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content' import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content'
interface YAMLException extends Error { interface YAMLException extends Error {

View File

@@ -2,7 +2,7 @@ import fs from 'fs'
import path from 'path' import path from 'path'
import chalk from 'chalk' import chalk from 'chalk'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
type Options = { type Options = {
verbose?: boolean verbose?: boolean

View File

@@ -38,7 +38,7 @@ import type { Page } from '@/types'
import warmServer from '@/frame/lib/warm-server' import warmServer from '@/frame/lib/warm-server'
import { getDeepDataByLanguage } from '@/data-directory/lib/get-data' import { getDeepDataByLanguage } from '@/data-directory/lib/get-data'
import { getLiquidTokens } from '@/content-linter/lib/helpers/liquid-utils' import { getLiquidTokens } from '@/content-linter/lib/helpers/liquid-utils'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content' import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content'
const EXCEPTIONS = new Set([ const EXCEPTIONS = new Set([

View File

@@ -3,7 +3,7 @@ import path from 'path'
import { afterAll, beforeAll, describe, expect, test } from 'vitest' import { afterAll, beforeAll, describe, expect, test } from 'vitest'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { import {
getDataByLanguage, getDataByLanguage,
getDeepDataByLanguage, getDeepDataByLanguage,

View File

@@ -2,7 +2,7 @@ import { expect, test, vi } from 'vitest'
import { get, getDOM } from '@/tests/helpers/e2etest' import { get, getDOM } from '@/tests/helpers/e2etest'
import { describeIfDocsEarlyAccess } from '@/tests/helpers/conditional-runs' import { describeIfDocsEarlyAccess } from '@/tests/helpers/conditional-runs'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
const VALID_EARLY_ACCESS_URI = '/early-access/github/save-time-with-slash-commands' const VALID_EARLY_ACCESS_URI = '/early-access/github/save-time-with-slash-commands'

View File

@@ -1,4 +1,4 @@
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { allVersionKeys } from '@/versions/lib/all-versions' import { allVersionKeys } from '@/versions/lib/all-versions'
import { productIds } from '@/products/lib/all-products' import { productIds } from '@/products/lib/all-products'
import { allTools } from '@/tools/lib/all-tools' import { allTools } from '@/tools/lib/all-tools'

View File

@@ -1,9 +1,9 @@
import path from 'path' import path from 'path'
import languages from '@/languages/lib/languages-server'
import type { Language } from '@/languages/lib/languages' import type { Language } from '@/languages/lib/languages'
import type { UnversionedTree, UnversionLanguageTree, SiteTree, Tree } from '@/types' import type { UnversionedTree, UnversionLanguageTree, SiteTree, Tree } from '@/types'
import languages from '@/languages/lib/languages'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
import createTree from './create-tree' import createTree from './create-tree'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version' import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'

View File

@@ -2,7 +2,7 @@ import type { NextFunction, Response } from 'express'
import type { ExtendedRequest, Context } from '@/types' import type { ExtendedRequest, Context } from '@/types'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases' import enterpriseServerReleases from '@/versions/lib/enterprise-server-releases'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
import { productMap } from '@/products/lib/all-products' import { productMap } from '@/products/lib/all-products'

View File

@@ -3,7 +3,7 @@ import type { Response, NextFunction } from 'express'
import type { ExtendedRequest } from '@/types' import type { ExtendedRequest } from '@/types'
import { getProductGroups } from '@/products/lib/get-product-groups' import { getProductGroups } from '@/products/lib/get-product-groups'
import warmServer from '@/frame/lib/warm-server' import warmServer from '@/frame/lib/warm-server'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { allVersionKeys } from '@/versions/lib/all-versions' import { allVersionKeys } from '@/versions/lib/all-versions'
const isHomepage = (path: string) => { const isHomepage = (path: string) => {

View File

@@ -4,7 +4,7 @@ import type { Response, NextFunction } from 'express'
import { ROOT } from '@/frame/lib/constants' import { ROOT } from '@/frame/lib/constants'
import Page from '@/frame/lib/page' import Page from '@/frame/lib/page'
import { languagePrefixPathRegex } from '@/languages/lib/languages' import { languagePrefixPathRegex } from '@/languages/lib/languages-server'
import type { ExtendedRequest } from '@/types' import type { ExtendedRequest } from '@/types'
interface FindPageOptions { interface FindPageOptions {

View File

@@ -1,6 +1,6 @@
import { shouldUseAppRouter, isVersionedPath } from '@/app/lib/routing-patterns' import { shouldUseAppRouter, isVersionedPath } from '@/app/lib/routing-patterns'
import { isArchivedVersion } from '@/archives/lib/is-archived-version' import { isArchivedVersion } from '@/archives/lib/is-archived-version'
import { languagePrefixPathRegex } from '@/languages/lib/languages' import { languagePrefixPathRegex } from '@/languages/lib/languages-server'
import versionSatisfiesRange from '@/versions/lib/version-satisfies-range' import versionSatisfiesRange from '@/versions/lib/version-satisfies-range'
import type { NextFunction, Request, Response } from 'express' import type { NextFunction, Request, Response } from 'express'
import helmet from 'helmet' import helmet from 'helmet'

View File

@@ -5,7 +5,7 @@ import type { ExtendedRequest } from '@/types'
import { defaultCacheControl } from '@/frame/middleware/cache-control' import { defaultCacheControl } from '@/frame/middleware/cache-control'
import catchMiddlewareError from '@/observability/middleware/catch-middleware-error' import catchMiddlewareError from '@/observability/middleware/catch-middleware-error'
import statsd from '@/observability/lib/statsd' import statsd from '@/observability/lib/statsd'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
const router = express.Router() const router = express.Router()

View File

@@ -19,7 +19,7 @@ import path from 'path'
import type { Response, NextFunction } from 'express' import type { Response, NextFunction } from 'express'
import type { ExtendedRequest, UnversionedTree, SiteTree } from '@/types' import type { ExtendedRequest, UnversionedTree, SiteTree } from '@/types'
import languages, { languageKeys } from '@/languages/lib/languages' import languages, { languageKeys } from '@/languages/lib/languages-server'
import createTree from '@/frame/lib/create-tree' import createTree from '@/frame/lib/create-tree'
import warmServer from '@/frame/lib/warm-server' import warmServer from '@/frame/lib/warm-server'
import { loadSiteTree, loadPages, loadPageMap } from '@/frame/lib/page-data' import { loadSiteTree, loadPages, loadPageMap } from '@/frame/lib/page-data'

View File

@@ -6,7 +6,7 @@ import { decode } from 'html-entities'
import { chain, pick } from 'lodash-es' import { chain, pick } from 'lodash-es'
import { loadPages } from '@/frame/lib/page-data' import { loadPages } from '@/frame/lib/page-data'
import libLanguages from '@/languages/lib/languages' import libLanguages from '@/languages/lib/languages-server'
import { liquid } from '@/content-render/index' import { liquid } from '@/content-render/index'
import patterns from '@/frame/lib/patterns' import patterns from '@/frame/lib/patterns'
import removeFPTFromPath from '@/versions/lib/remove-fpt-from-path' import removeFPTFromPath from '@/versions/lib/remove-fpt-from-path'

View File

@@ -17,7 +17,7 @@ import createApp from '@/frame/lib/app'
import EnterpriseServerReleases from '@/versions/lib/enterprise-server-releases' import EnterpriseServerReleases from '@/versions/lib/enterprise-server-releases'
import loadRedirects from '@/redirects/lib/precompile' import loadRedirects from '@/redirects/lib/precompile'
import { loadPageMap, loadPages } from '@/frame/lib/page-data' import { loadPageMap, loadPages } from '@/frame/lib/page-data'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { RewriteAssetPathsPlugin } from '@/ghes-releases/scripts/deprecate/rewrite-asset-paths' import { RewriteAssetPathsPlugin } from '@/ghes-releases/scripts/deprecate/rewrite-asset-paths'
import Page from '@/frame/lib/page' import Page from '@/frame/lib/page'

View File

@@ -3,7 +3,7 @@ import {
readCompressedJsonFileFallback, readCompressedJsonFileFallback,
} from '@/frame/lib/read-json-file' } from '@/frame/lib/read-json-file'
import { getAutomatedPageMiniTocItems } from '@/frame/lib/get-mini-toc-items' import { getAutomatedPageMiniTocItems } from '@/frame/lib/get-mini-toc-items'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
interface GraphqlContext { interface GraphqlContext {
currentLanguage: string currentLanguage: string

View File

@@ -1,67 +0,0 @@
import type { LanguageItem } from '@/languages/components/LanguagesContext'
/**
* Client-safe language data extracted from src/languages/lib/languages.ts.
* Only used by frontend components.
* Does not include server-side logic or Node.js-specific fs or path operations.
*/
export const clientLanguages: Record<string, LanguageItem> = {
en: {
name: 'English',
code: 'en',
nativeName: 'English',
hreflang: 'en',
},
es: {
name: 'Spanish',
code: 'es',
nativeName: 'Español',
hreflang: 'es',
},
ja: {
name: 'Japanese',
code: 'ja',
nativeName: '日本語',
hreflang: 'ja',
},
pt: {
name: 'Portuguese',
code: 'pt',
nativeName: 'Português do Brasil',
hreflang: 'pt',
},
zh: {
name: 'Simplified Chinese',
code: 'zh',
nativeName: '简体中文',
hreflang: 'zh-Hans',
},
ru: {
name: 'Russian',
code: 'ru',
nativeName: 'Русский',
hreflang: 'ru',
},
fr: {
name: 'French',
code: 'fr',
nativeName: 'Français',
hreflang: 'fr',
},
ko: {
name: 'Korean',
code: 'ko',
nativeName: '한국어',
hreflang: 'ko',
},
de: {
name: 'German',
code: 'de',
nativeName: 'Deutsch',
hreflang: 'de',
},
}
export const clientLanguageKeys: string[] = Object.keys(clientLanguages)
export type ClientLanguageCode = keyof typeof clientLanguages

View File

@@ -1,7 +1,7 @@
import fs from 'fs/promises' import fs from 'fs/promises'
import path from 'path' import path from 'path'
import yaml from 'js-yaml' import yaml from 'js-yaml'
import languages from './languages' import languages from './languages-server'
const cache: Record<string, any> = {} const cache: Record<string, any> = {}

View File

@@ -0,0 +1,97 @@
/*
This file adds the following properties to languages in ./languages.ts:
- dir: string
This file will also remove languages for local development and tests
that have not be specified by ENABLED_LANGUAGES
*/
import path from 'path'
import fs from 'fs'
import dotenv from 'dotenv'
import { ROOT, TRANSLATIONS_ROOT, TRANSLATIONS_FIXTURE_ROOT } from '@/frame/lib/constants'
import { languages as baseLanguages, type Language as BaseLanguage } from './languages'
dotenv.config({ quiet: true })
// Server-side language extends base language with required dir property
export interface Language extends BaseLanguage {
dir: string
}
export interface Languages {
[code: string]: Language
}
function getRoot(languageCode: string): string {
if (languageCode === 'en') return ROOT
// This one trumps anything else. This makes it possible, and convenient,
// for running tests that depends on testing translations based on
// fixtures exclusively.
if (TRANSLATIONS_FIXTURE_ROOT) {
return path.join(TRANSLATIONS_FIXTURE_ROOT, languageCode)
}
// example: process.env.TRANSLATIONS_ROOT_ES_ES
const possibleEnvVar =
process.env[`TRANSLATIONS_ROOT_${languageCode.toUpperCase().replace(/-/g, '_')}`]
if (possibleEnvVar) {
return possibleEnvVar
}
// Default
return path.join(TRANSLATIONS_ROOT, languageCode)
}
// Build server languages with directory paths
const allLanguagesWithDirs: Languages = {}
for (const [code, lang] of Object.entries(baseLanguages)) {
allLanguagesWithDirs[code] = {
...lang,
dir: getRoot(lang.locale || code),
}
}
Object.freeze(allLanguagesWithDirs)
const languages: Languages = { ...allLanguagesWithDirs }
if (TRANSLATIONS_FIXTURE_ROOT) {
// Keep all languages that have a directory in the fixture root.
Object.entries(languages).forEach(([code, { dir }]) => {
if (code !== 'en' && !fs.existsSync(dir)) {
delete languages[code]
}
})
} else if (process.env.ENABLED_LANGUAGES) {
if (process.env.ENABLED_LANGUAGES.toLowerCase() !== 'all') {
Object.keys(languages).forEach((code) => {
if (!process.env.ENABLED_LANGUAGES!.includes(code)) {
delete languages[code]
}
})
// This makes the translation health report not valid JSON
// console.log(`ENABLED_LANGUAGES: ${process.env.ENABLED_LANGUAGES}`)
}
} else if (process.env.NODE_ENV === 'test') {
// Unless explicitly set, when running tests default to just English
Object.keys(languages).forEach((code) => {
if (code !== 'en') delete languages[code]
})
}
export const languageKeys: string[] = Object.keys(languages)
export const languagePrefixPathRegex: RegExp = new RegExp(`^/(${languageKeys.join('|')})(/|$)`)
/** Return true if the URL is something like /en/foo or /ja but return false
* if it's something like /foo or /foo/bar or /fr (because French (fr)
* is currently not an active language)
*/
export function pathLanguagePrefixed(path: string): boolean {
return languagePrefixPathRegex.test(path)
}
export default languages

View File

@@ -1,26 +1,15 @@
// See also languages-schema.ts // See also languages-schema.ts
// Nota bene: If you are adding a new language, // Nota bene: If you are adding a new language,
// change accept-language handling in CDN config as well. // change accept-language handling in CDN config as well.
import path from 'path'
import fs from 'fs'
import dotenv from 'dotenv' /**
* Client-safe language definitions without server-side dependencies.
import { ROOT, TRANSLATIONS_ROOT, TRANSLATIONS_FIXTURE_ROOT } from '@/frame/lib/constants' * For server-side usage with fs/path operations, import from './languages-server.ts'
*/
dotenv.config({ quiet: true })
export interface Language {
name: string
nativeName?: string
code: string
hreflang: string
redirectPatterns?: RegExp[]
dir: string
}
export type LanguageCode = 'en' | 'es' | 'ja' | 'pt' | 'zh' | 'ru' | 'fr' | 'ko' | 'de' export type LanguageCode = 'en' | 'es' | 'ja' | 'pt' | 'zh' | 'ru' | 'fr' | 'ko' | 'de'
export type LocaleCode = export type LocaleCode =
| 'en'
| 'es-es' | 'es-es'
| 'ja-jp' | 'ja-jp'
| 'pt-br' | 'pt-br'
@@ -30,57 +19,35 @@ export type LocaleCode =
| 'ko-kr' | 'ko-kr'
| 'de-de' | 'de-de'
export interface Language {
name: string
nativeName?: string
code: LanguageCode
hreflang: string
locale: LocaleCode
redirectPatterns?: RegExp[]
dir?: string
}
export interface Languages { export interface Languages {
[code: string]: Language [code: string]: Language
} }
const possibleEnvVars: Record<LocaleCode, string | undefined> = {
'es-es': process.env.TRANSLATIONS_ROOT_ES_ES,
'ja-jp': process.env.TRANSLATIONS_ROOT_JA_JP,
'pt-br': process.env.TRANSLATIONS_ROOT_PT_BR,
'zh-cn': process.env.TRANSLATIONS_ROOT_ZH_CN,
'ru-ru': process.env.TRANSLATIONS_ROOT_RU_RU,
'fr-fr': process.env.TRANSLATIONS_ROOT_FR_FR,
'ko-kr': process.env.TRANSLATIONS_ROOT_KO_KR,
'de-de': process.env.TRANSLATIONS_ROOT_DE_DE,
}
function getRoot(languageCode: string): string {
if (languageCode === 'en') return ROOT
// This one trumps anything else. This makes it possible, and convenient,
// for running tests that depends on testing translations based on
// fixtures exclusively.
if (TRANSLATIONS_FIXTURE_ROOT) {
return path.join(TRANSLATIONS_FIXTURE_ROOT, languageCode)
}
if (languageCode in possibleEnvVars) {
const possibleEnvVar = possibleEnvVars[languageCode as LocaleCode]
if (possibleEnvVar) {
return possibleEnvVar
}
} else {
console.warn(`Not recognized languageCode '${languageCode}'`)
}
// Default
return path.join(TRANSLATIONS_ROOT, languageCode)
}
// Languages in order of accept-language header frequency // Languages in order of accept-language header frequency
const allLanguages: Languages = { // Note: 'dir' is omitted here as it requires server-side path resolution
export const languages: Languages = {
en: { en: {
name: 'English', name: 'English',
code: 'en', code: 'en',
hreflang: 'en', hreflang: 'en',
dir: getRoot('en'), locale: 'en',
}, },
es: { es: {
name: 'Spanish', name: 'Spanish',
nativeName: 'Español', nativeName: 'Español',
code: 'es', code: 'es',
hreflang: 'es', hreflang: 'es',
dir: getRoot('es-es'), locale: 'es-es',
}, },
ja: { ja: {
name: 'Japanese', name: 'Japanese',
@@ -88,7 +55,7 @@ const allLanguages: Languages = {
code: 'ja', code: 'ja',
hreflang: 'ja', hreflang: 'ja',
redirectPatterns: [/^\/jp/], redirectPatterns: [/^\/jp/],
dir: getRoot('ja-jp'), locale: 'ja-jp',
}, },
pt: { pt: {
name: 'Portuguese', name: 'Portuguese',
@@ -96,7 +63,7 @@ const allLanguages: Languages = {
code: 'pt', code: 'pt',
hreflang: 'pt', hreflang: 'pt',
redirectPatterns: [/^\/br/], redirectPatterns: [/^\/br/],
dir: getRoot('pt-br'), locale: 'pt-br',
}, },
zh: { zh: {
name: 'Simplified Chinese', name: 'Simplified Chinese',
@@ -104,21 +71,21 @@ const allLanguages: Languages = {
code: 'zh', code: 'zh',
hreflang: 'zh-Hans', hreflang: 'zh-Hans',
redirectPatterns: [/^\/cn/, /^\/zh-\w{2}/], redirectPatterns: [/^\/cn/, /^\/zh-\w{2}/],
dir: getRoot('zh-cn'), locale: 'zh-cn',
}, },
ru: { ru: {
name: 'Russian', name: 'Russian',
nativeName: 'Русский', nativeName: 'Русский',
code: 'ru', code: 'ru',
hreflang: 'ru', hreflang: 'ru',
dir: getRoot('ru-ru'), locale: 'ru-ru',
}, },
fr: { fr: {
name: 'French', name: 'French',
nativeName: 'Français', nativeName: 'Français',
code: 'fr', code: 'fr',
hreflang: 'fr', hreflang: 'fr',
dir: getRoot('fr-fr'), locale: 'fr-fr',
}, },
ko: { ko: {
name: 'Korean', name: 'Korean',
@@ -126,59 +93,16 @@ const allLanguages: Languages = {
code: 'ko', code: 'ko',
hreflang: 'ko', hreflang: 'ko',
redirectPatterns: [/^\/kr/], redirectPatterns: [/^\/kr/],
dir: getRoot('ko-kr'), locale: 'ko-kr',
}, },
de: { de: {
name: 'German', name: 'German',
nativeName: 'Deutsch', nativeName: 'Deutsch',
code: 'de', code: 'de',
hreflang: 'de', hreflang: 'de',
dir: getRoot('de-de'), locale: 'de-de',
}, },
} }
// Some markdownlint tests depend on having access to all Object.freeze(languages)
// language keys. Not modifying the original object makes
// it possible to export all keys, even when those directories
// don't exist on disk.
Object.freeze(allLanguages)
export const allLanguageKeys: string[] = Object.keys(allLanguages)
const languages: Languages = { ...allLanguages }
if (TRANSLATIONS_FIXTURE_ROOT) {
// Keep all languages that have a directory in the fixture root.
Object.entries(languages).forEach(([code, { dir }]) => {
if (code !== 'en' && !fs.existsSync(dir)) {
delete languages[code]
}
})
} else if (process.env.ENABLED_LANGUAGES) {
if (process.env.ENABLED_LANGUAGES.toLowerCase() !== 'all') {
Object.keys(languages).forEach((code) => {
if (!process.env.ENABLED_LANGUAGES!.includes(code)) {
delete languages[code]
}
})
// This makes the translation health report not valid JSON
// console.log(`ENABLED_LANGUAGES: ${process.env.ENABLED_LANGUAGES}`)
}
} else if (process.env.NODE_ENV === 'test') {
// Unless explicitly set, when running tests default to just English
Object.keys(languages).forEach((code) => {
if (code !== 'en') delete languages[code]
})
}
export const languageKeys: string[] = Object.keys(languages) export const languageKeys: string[] = Object.keys(languages)
export const languagePrefixPathRegex: RegExp = new RegExp(`^/(${languageKeys.join('|')})(/|$)`)
/** Return true if the URL is something like /en/foo or /ja but return false
* if it's something like /foo or /foo/bar or /fr (because French (fr)
* is currently not an active language)
*/
export function pathLanguagePrefixed(path: string): boolean {
return languagePrefixPathRegex.test(path)
}
export default languages

View File

@@ -2,7 +2,7 @@ import type { Request, Response, NextFunction } from 'express'
import parser from 'accept-language-parser' import parser from 'accept-language-parser'
import type { Language as parserLanguage } from 'accept-language-parser' import type { Language as parserLanguage } from 'accept-language-parser'
import languages, { languageKeys } from '@/languages/lib/languages' import languages, { languageKeys } from '@/languages/lib/languages-server'
import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants' import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants'
import type { ExtendedRequest, Languages } from '@/types' import type { ExtendedRequest, Languages } from '@/types'
import { updateLoggerContext } from '@/observability/logger/lib/logger-context' import { updateLoggerContext } from '@/observability/logger/lib/logger-context'

View File

@@ -7,7 +7,7 @@ import { TokenizationError } from 'liquidjs'
import walk from 'walk-sync' import walk from 'walk-sync'
import { getLiquidTokens } from '@/content-linter/lib/helpers/liquid-utils' import { getLiquidTokens } from '@/content-linter/lib/helpers/liquid-utils'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import warmServer from '@/frame/lib/warm-server' import warmServer from '@/frame/lib/warm-server'
import type { Site } from '@/types' import type { Site } from '@/types'
import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content' import { correctTranslatedContentStrings } from '@/languages/lib/correct-translation-content'

View File

@@ -1,4 +1,4 @@
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { makeLanguageSurrogateKey } from '@/frame/middleware/set-fastly-surrogate-key' import { makeLanguageSurrogateKey } from '@/frame/middleware/set-fastly-surrogate-key'
import purgeEdgeCache from '@/workflows/purge-edge-cache' import purgeEdgeCache from '@/workflows/purge-edge-cache'

View File

@@ -1,4 +1,4 @@
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { describe, expect, test, vi } from 'vitest' import { describe, expect, test, vi } from 'vitest'
describe('files', () => { describe('files', () => {

View File

@@ -1,6 +1,6 @@
import { describe, expect, test, vi } from 'vitest' import { describe, expect, test, vi } from 'vitest'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { blockIndex } from '@/frame/middleware/block-robots' import { blockIndex } from '@/frame/middleware/block-robots'
import { get, getDOMCached as getDOM } from '@/tests/helpers/e2etest' import { get, getDOMCached as getDOM } from '@/tests/helpers/e2etest'
import Page from '@/frame/lib/page' import Page from '@/frame/lib/page'

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { getDOM } from '@/tests/helpers/e2etest' import { getDOM } from '@/tests/helpers/e2etest'
const langs = languageKeys.filter((lang) => lang !== 'en') const langs = languageKeys.filter((lang) => lang !== 'en')

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { get } from '@/tests/helpers/e2etest' import { get } from '@/tests/helpers/e2etest'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
const langs = languageKeys.filter((lang) => lang !== 'en') const langs = languageKeys.filter((lang) => lang !== 'en')

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { get } from '@/tests/helpers/e2etest' import { get } from '@/tests/helpers/e2etest'
import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants' import { USER_LANGUAGE_COOKIE_NAME } from '@/frame/lib/constants'

View File

@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest' import { describe, expect, test } from 'vitest'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { get } from '@/tests/helpers/e2etest' import { get } from '@/tests/helpers/e2etest'
const langs = languageKeys.filter((lang) => lang !== 'en') const langs = languageKeys.filter((lang) => lang !== 'en')

View File

@@ -6,7 +6,7 @@ import { productMap, data } from '@/products/lib/all-products'
import { renderContentWithFallback } from '@/languages/lib/render-with-fallback' import { renderContentWithFallback } from '@/languages/lib/render-with-fallback'
import removeFPTFromPath from '@/versions/lib/remove-fpt-from-path' import removeFPTFromPath from '@/versions/lib/remove-fpt-from-path'
import frontmatter from '@/frame/lib/read-frontmatter' import frontmatter from '@/frame/lib/read-frontmatter'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
type PageMap = Record<string, Page> type PageMap = Record<string, Page>

View File

@@ -1,4 +1,4 @@
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version' import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
import { import {

View File

@@ -1,7 +1,7 @@
import type { NextFunction, Response } from 'express' import type { NextFunction, Response } from 'express'
import patterns from '@/frame/lib/patterns' import patterns from '@/frame/lib/patterns'
import { pathLanguagePrefixed } from '@/languages/lib/languages' import { pathLanguagePrefixed } from '@/languages/lib/languages-server'
import { deprecatedWithFunctionalRedirects } from '@/versions/lib/enterprise-server-releases' import { deprecatedWithFunctionalRedirects } from '@/versions/lib/enterprise-server-releases'
import getRedirect from '../lib/get-redirect' import getRedirect from '../lib/get-redirect'
import { defaultCacheControl, languageCacheControl } from '@/frame/middleware/cache-control' import { defaultCacheControl, languageCacheControl } from '@/frame/middleware/cache-control'

View File

@@ -1,6 +1,6 @@
import type { NextFunction, Response } from 'express' import type { NextFunction, Response } from 'express'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { defaultCacheControl } from '@/frame/middleware/cache-control' import { defaultCacheControl } from '@/frame/middleware/cache-control'
import { ExtendedRequest } from '@/types' import { ExtendedRequest } from '@/types'

View File

@@ -4,7 +4,7 @@ import path from 'path'
import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file' import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file'
import { getAutomatedPageMiniTocItems } from '@/frame/lib/get-mini-toc-items' import { getAutomatedPageMiniTocItems } from '@/frame/lib/get-mini-toc-items'
import { allVersions, getOpenApiVersion } from '@/versions/lib/all-versions' import { allVersions, getOpenApiVersion } from '@/versions/lib/all-versions'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import type { Context } from '@/types' import type { Context } from '@/types'
import type { Operation } from '@/rest/components/types' import type { Operation } from '@/rest/components/types'

View File

@@ -1,4 +1,4 @@
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { utcTimestamp } from '@/search/lib/helpers/time' import { utcTimestamp } from '@/search/lib/helpers/time'
import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions' import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions'

View File

@@ -3,7 +3,7 @@
we need to validate and parse the parameters. This file contains the configuration for which parameters we need to validate and parse the parameters. This file contains the configuration for which parameters
to expect based on the type of search request "e.g. general search vs autocomplete search" and how to validate them. to expect based on the type of search request "e.g. general search vs autocomplete search" and how to validate them.
*/ */
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions' import { allIndexVersionKeys, versionToIndexVersionMap } from '@/search/lib/elasticsearch-versions'
import { SearchTypes } from '@/search/types' import { SearchTypes } from '@/search/types'

View File

@@ -10,7 +10,7 @@ import { Command, Option } from 'commander'
import chalk from 'chalk' import chalk from 'chalk'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { allVersions } from '@/versions/lib/all-versions' import { allVersions } from '@/versions/lib/all-versions'
import type { IndicesAnalyzeAnalyzeToken } from '@elastic/elasticsearch/lib/api/types' import type { IndicesAnalyzeAnalyzeToken } from '@elastic/elasticsearch/lib/api/types'

View File

@@ -2,7 +2,7 @@ import { program, Option, Command, InvalidArgumentError } from 'commander'
import { errors } from '@elastic/elasticsearch' import { errors } from '@elastic/elasticsearch'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { indexGeneralSearch } from './lib/index-general-search' import { indexGeneralSearch } from './lib/index-general-search'
import { import {

View File

@@ -1,6 +1,6 @@
import { Client } from '@elastic/elasticsearch' import { Client } from '@elastic/elasticsearch'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes' import { getElasticSearchIndex } from '@/search/lib/elasticsearch-indexes'
import { getElasticsearchClient } from '@/search/lib/helpers/get-client' import { getElasticsearchClient } from '@/search/lib/helpers/get-client'
import { import {

View File

@@ -3,7 +3,7 @@ import chalk from 'chalk'
import dotenv from 'dotenv' import dotenv from 'dotenv'
import boxen from 'boxen' import boxen from 'boxen'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import parsePageSectionsIntoRecords from '@/search/scripts/scrape/lib/parse-page-sections-into-records' import parsePageSectionsIntoRecords from '@/search/scripts/scrape/lib/parse-page-sections-into-records'
import getPopularPages from '@/search/scripts/scrape/lib/popular-pages' import getPopularPages from '@/search/scripts/scrape/lib/popular-pages'
import domwaiter from '@/search/scripts/scrape/lib/domwaiter' import domwaiter from '@/search/scripts/scrape/lib/domwaiter'

View File

@@ -1,6 +1,6 @@
import chalk from 'chalk' import chalk from 'chalk'
import languages from '@/languages/lib/languages' import languages from '@/languages/lib/languages-server'
import buildRecords from '@/search/scripts/scrape/lib/build-records' import buildRecords from '@/search/scripts/scrape/lib/build-records'
import findIndexablePages from '@/search/scripts/scrape/lib/find-indexable-pages' import findIndexablePages from '@/search/scripts/scrape/lib/find-indexable-pages'
import { writeIndexRecords } from '@/search/scripts/scrape/lib/search-index-records' import { writeIndexRecords } from '@/search/scripts/scrape/lib/search-index-records'

View File

@@ -4,7 +4,7 @@
import { existsSync, statSync, readdirSync } from 'fs' import { existsSync, statSync, readdirSync } from 'fs'
import { program, Option } from 'commander' import { program, Option } from 'commander'
import { languageKeys } from '@/languages/lib/languages' import { languageKeys } from '@/languages/lib/languages-server'
import scrapeIntoIndexJson from '@/search/scripts/scrape/lib/scrape-into-index-json' import scrapeIntoIndexJson from '@/search/scripts/scrape/lib/scrape-into-index-json'
import { import {
allIndexVersionOptions, allIndexVersionOptions,

View File

@@ -3,6 +3,7 @@ import type { Failbot } from '@github/failbot'
import type enterpriseServerReleases from '@/versions/lib/enterprise-server-releases.d' import type enterpriseServerReleases from '@/versions/lib/enterprise-server-releases.d'
import type { ValidOcticon } from '@/landings/types' import type { ValidOcticon } from '@/landings/types'
import type { Language, Languages } from '@/languages/lib/languages-server'
import type { MiniTocItem } from '@/frame/lib/get-mini-toc-items' import type { MiniTocItem } from '@/frame/lib/get-mini-toc-items'
// Shared type for resolved article information used across landing pages and carousels // Shared type for resolved article information used across landing pages and carousels
@@ -325,16 +326,8 @@ export type SecretScanningData = {
isduplicate: boolean isduplicate: boolean
} }
type Language = { // Language and Languages types are imported at the top from languages-server
name: string export type { Language, Languages }
code: string
hreflang: string
dir: string
}
export type Languages = {
[key: string]: Language
}
export type Permalink = { export type Permalink = {
languageCode: string languageCode: string