Skip to content

Commit

Permalink
fix middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
KishiTheMechanic committed Oct 26, 2024
1 parent 5c1ddf7 commit 2eda59d
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 36 deletions.
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@elsoul/fresh-i18n",
"version": "0.6.1",
"version": "0.7.0",
"description": "A simple and flexible internationalization (i18n) plugin for Deno's Fresh framework.",
"runtimes": ["deno", "browser"],
"exports": "./mod.ts",
Expand Down
51 changes: 19 additions & 32 deletions src/i18nPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { pathname, translationData } from '@/src/store.ts'
import type { MiddlewareFn } from '@/src/types.ts'

/**
* Options for configuring the i18n plugin.
* Configuration options for the i18n plugin.
*
* @property languages - Array of supported language codes (e.g., ['en', 'ja']).
* @property defaultLanguage - Default language code used when no language is detected.
* @property localesDir - Directory path where translation JSON files are stored.
* @property languages - An array of supported language codes (e.g., ['en', 'ja']).
* @property defaultLanguage - The default language code to use when no language is detected.
* @property localesDir - The directory path where translation JSON files are stored.
*/
export interface I18nOptions {
languages: string[]
Expand All @@ -16,37 +16,25 @@ export interface I18nOptions {
}

/**
* Reads a JSON file and parses its contents, ensuring all values are strings.
* Reads a JSON file and parses its contents.
*
* @param filePath - Path to the JSON file.
* @returns Parsed JSON object as Record<string, string>, or an empty object if invalid.
* @param filePath - The path to the JSON file.
* @returns Parsed JSON object as Record<string, string>, or an empty object if parsing fails.
*/
async function readJsonFile(filePath: string): Promise<Record<string, string>> {
const content = await Deno.readTextFile(filePath)
try {
const data = JSON.parse(content)
return isStringRecord(data) ? data : {}
return JSON.parse(content) as Record<string, string>
} catch {
return {}
}
}

/**
* Type guard to check if an object is Record<string, string>.
*
* @param data - The data to validate.
* @returns True if data is Record<string, string>, otherwise false.
*/
function isStringRecord(data: unknown): data is Record<string, string> {
return typeof data === 'object' && data !== null &&
Object.values(data).every((value) => typeof value === 'string')
}

/**
* Middleware function to initialize internationalization (i18n) support.
* This plugin detects the user's language based on the URL, loads the necessary
* translation files dynamically, and saves the translations and base path as global
* signals for client-side and server-side access.
* translation files dynamically, and saves the translations and base path as
* global signals for both client-side and server-side access.
*
* @param options - Configuration options for the i18n plugin.
* @returns A middleware function that handles language detection and translation loading.
Expand All @@ -56,29 +44,28 @@ export const i18nPlugin = (
): MiddlewareFn<
{ t: Record<string, Record<string, string>>; path: string }
> => {
return async (ctx, next) => {
return async (ctx) => {
const url = new URL(ctx.req.url)
const pathSegments = url.pathname.split('/').filter(Boolean)
const lang = languages.includes(pathSegments[0])
? pathSegments[0]
: defaultLanguage

// Set the root path without language prefix for use in client-side navigation
// Sets the root path without the language prefix for client-side navigation.
const rootPath = lang === pathSegments[0]
? '/' + pathSegments.slice(1).join('/')
: url.pathname

ctx.state.path = rootPath // Server-side context
pathname.value = rootPath // Global signal for client-side
ctx.state.path = rootPath
pathname.value = rootPath

// Initialize translation data
const translationDataSSR: Record<string, Record<string, string>> = {}

/**
* Loads a translation namespace by reading the corresponding JSON file from localesDir.
* Loads a translation namespace by reading the corresponding JSON file from `localesDir`.
* If the file does not exist, it is ignored.
*
* @param namespace - Namespace for the translation file to load (e.g., 'common').
* @param namespace - The namespace of the translation file to load (e.g., 'common').
*/
const loadTranslation = async (namespace: string) => {
try {
Expand All @@ -90,18 +77,18 @@ export const i18nPlugin = (
}
}

// Load common namespace and additional namespaces based on the URL path
// Load the common namespace and additional namespaces based on the URL path.
await loadTranslation('common')
for (
const segment of pathSegments.slice(lang === pathSegments[0] ? 1 : 0)
) {
await loadTranslation(segment)
}

// Set translation data in both server context and client-side global signal
ctx.state.t = translationDataSSR
translationData.value = translationDataSSR

await next()
// Call `ctx.next()` to continue to the next middleware in the chain.
await ctx.next()
}
}
6 changes: 3 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// types.ts

/**
* Represents the context passed to every middleware function.
*
Expand All @@ -9,7 +11,7 @@
export interface FreshContext<State> {
req: Request
state: State
next: () => Promise<Response | void>
next: () => Promise<Response | void> // `next`を追加
}

/**
Expand All @@ -18,12 +20,10 @@ export interface FreshContext<State> {
*
* @template State - The state type for the middleware.
* @param ctx - The FreshContext with the current request and state.
* @param next - Function to call the next middleware.
* @returns A promise resolving to a `Response` or void.
*/
export type MiddlewareFn<State = TranslationState> = (
ctx: FreshContext<State>,
next: () => Promise<Response | void>,
) => Promise<Response | void>

/**
Expand Down

0 comments on commit 2eda59d

Please sign in to comment.