{"id":20345990,"url":"https://github.com/lupcode/node-lup-language","last_synced_at":"2025-09-23T09:32:00.681Z","repository":{"id":54652120,"uuid":"409558450","full_name":"LupCode/node-lup-language","owner":"LupCode","description":"NodeJS express middleware for multi-language websites","archived":false,"fork":false,"pushed_at":"2024-12-16T10:26:32.000Z","size":193,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-01-07T18:42:57.695Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LupCode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-23T11:10:54.000Z","updated_at":"2024-12-16T10:26:36.000Z","dependencies_parsed_at":"2024-10-25T01:50:17.989Z","dependency_job_id":"f1717952-f047-4421-9e36-5bdea6628c6d","html_url":"https://github.com/LupCode/node-lup-language","commit_stats":{"total_commits":50,"total_committers":4,"mean_commits":12.5,"dds":"0.30000000000000004","last_synced_commit":"566fabb9622228c9585dfed607dc172756aba838"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LupCode%2Fnode-lup-language","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LupCode%2Fnode-lup-language/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LupCode%2Fnode-lup-language/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LupCode%2Fnode-lup-language/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LupCode","download_url":"https://codeload.github.com/LupCode/node-lup-language/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233962721,"owners_count":18757915,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T22:10:40.274Z","updated_at":"2025-09-23T09:32:00.660Z","avatar_url":"https://github.com/LupCode.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![GitHub package.json version](https://img.shields.io/github/package-json/v/LupCode/node-lup-language)\n![npm bundle size](https://img.shields.io/bundlephobia/min/lup-language)\n![GitHub Workflow Status](https://img.shields.io/github/workflow/status/LupCode/node-lup-language/On%20Push)\n![NPM](https://img.shields.io/npm/l/lup-language)\n\n# lup-language\nNode express middleware for detecting requested language based on:\n1. URI prefix e.g. /**en**/home/\n2. Cookie value e.g. lang=en\n3. HTTP header e.g. Accept-Language=en-US,en;q=0.5\n4. Default language code\nRequest will be modified such that URI does not start with language code and new attribute `lang` gets added to the request object.\n\n## Request object after LanguageRouter\n* `req.lang` - requested language code e.g. 'en'\n* `req.langs` - list of all supported language codes (based on found translation files in `translations` dir or `next.config.js`).\n* `req.TEXT` - object with all translations for the requested language code (Disabled by default!).\n* `req.PATH` - update URI path without language code prefix.\n\n\n## Examples\n\n### Express\n`server.js`\n```javascript\nconst express = require('express');\nconst {LanguageRouter} = require('lup-language');\n\nconst app = express();\n\n// Add LanguageRouter as middleware that modifies request object \n// based on given options (see documentation).\n// Here parameter 'languages' is a list of language codes that \n// your app will accept.\napp.use(LanguageRouter({\n    defaultLanguage: 'en',\n}));\n\n// Create your custom routes and retriev request language code \n// by reading it from req.lang\napp.get('/', function(req, res){\n    res.send(\"Your requested language is: \"+req.lang.toUpperCase()+\"\u003cbr\u003e\"+\n             \"You can access translations from the translations dir using: \"+req.TEXT['TranslationKey']);\n});\n\napp.listen(80, function(){\n    console.log(\"Server running\");\n});\n```\n\n\n### Next.js ≥13 Page\n`app/[locale]/translations.ts`\n```typescript\n'use server';\nimport \"server-only\";\nimport { getTranslations } from \"lup-language\";\n\nexport default async function loadTranslations(locale: string, translationKeys: string[]): Promise\u003c{[key: string]: string}\u003e {\n    return await getTranslations(locale, 'en', translationKeys); // second argument is default locale\n};\n```\n\n`app/[locale]/layout.tsx`\n```typescript\n\nexport default async function RootLayout({ params, children }: LocaleLayoutProps){\n    const { locale } = await params;\n\n    // if locale is not supported redirect to a supported one\n    const validLocale = await checkLanguage(locale, getDefaultLocale());\n    if(validLocale \u0026\u0026 locale !== validLocale){\n        permanentRedirect('/'+validLocale+'/'); // response code 308 - Permanent Redirect\n    }\n\n    // load translations for given translation keys (note this loadTranslations function is defined in translations.ts, see below) \n    const TEXT = await loadTranslations(locale, [\n        'HelloWorld',\n    ]);\n\n    return \u003chtml lang={locale}\u003e\n        \u003cbody\u003e\n            {TEXT['HelloWorld']}\n        \u003c/body\u003e\n    \u003c/html\u003e;\n}\n\nexport async function generateStaticParams(context: StaticParamsContext){\n  const locales = await getLocales();\n  return locales.map((locale) =\u003e ({ locale: locale }));\n}\n```\n\n`app/[locale]/page.tsx`\n```typescript\nimport React from \"react\";\nimport styles from './page.module.css';\nimport loadTranslations from \"../translations\";\nimport { StaticParamsContext } from \"../../../services/Types.service\";\n\nexport default async function Home({ params }: StaticParamsContext) {\n  const locale = params.locale;\n  const TEXT = await loadTranslations(locale, ['HelloWorld']);\n\n  return \u003c\u003e\n    \u003cb\u003e{TEXT['HelloWorld']}\u003c/b\u003e\n  \u003c/\u003e\n}\n```\n\n`app/[locale]/translations.ts`\nServer action that also allows client components to load translations.\n```typescript\n'use server';\nimport 'server-only';\n\nimport { getTranslations } from \"lup-language\";\n\nexport default async function loadTranslations(locale: string, translationKeys: string[]): Promise\u003c{[key: string]: string}\u003e {\n    return await getTranslations(locale, getDefaultLocale(), translationKeys);\n};\n```\n\nOptional if unsupported languages or root should be redirected:  \n`middleware.ts`\n```typescript\nimport { NextResponse } from \"next/server\";\nimport type { NextRequest } from \"next/server\";\nimport { LanguageRouter } from \"lup-language\";\n\n// Settings\nconst lupLang = LanguageRouter({\n    defaultLanguage: 'en',\n});\n\nexport function middleware(request: NextRequest): NextResponse {\n\n    // Redirect to correct language\n    const langInfo = lupLang.nextJsMiddlewareHandler(request);\n    if(langInfo.redirect || langInfo.cookie){\n        const langResponse = langInfo.redirect ? NextResponse.redirect(langInfo.redirect, { status: langInfo.redirectResponseCode }) : NextResponse.next();\n        if(langInfo.cookie){\n            langResponse.cookies.set(langInfo.cookie.name, langInfo.cookie.value, langInfo.cookie.options);\n        }\n        return langResponse;\n    }\n\n\n    // Other middleware logic\n\n    return NextResponse.next();\n}\n```\n\n\n### Next.js with Express\n`server.ts`\n```typescript\nimport express from 'express';\nimport next from 'next';\n\nconst dev = Config.isDevMode();\nconst HTTP_BIND = process.env.HTTP_BIND || \"0.0.0.0\";\nconst HTTP_PORT = parseInt(process.env.HTTP_PORT || \"80\") || 80;\n\nconst nextApp = next({dev: dev});\nconst nextHandler = nextApp.getRequestHandler();\n\nnextApp.prepare().then(async () =\u003e {\n    const app = express();\n\n    // language middleware\n    app.use(await LanguageRouter({\n        useNextLanguages: false, /* true if Next ≤12 and i18n */\n        languagesFromTranslations: true,\n        redirectRoot: true, \n    }));\n\n    // TODO add here your custom routes ...\n\n    // all frontend routes\n    app.all('*', (req: Request | any, res: Response) =\u003e {\n        \n        // add language prefix back to url (got removed by LanguageRouter)\n        const idx1 = req.url.lastIndexOf(\".\"), idx2 = req.url.lastIndexOf(\"/\");\n        req.url = (idx1 \u003e idx2 || req.url.startsWith(\"/\"+req.lang) || req.url.startsWith(\"/_next\")) ? \n                    req.originalUrl : \"/\"+req.lang+req.url;\n\n        return nextHandler(req, res);\n    });\n\n    // start server\n    app.listen(HTTP_PORT, HTTP_BIND, function(){\n        console.log(\"Server running at \"+HTTP_BIND+\":\"+HTTP_PORT+\" in \"+(dev ? \"development\" : \"production\")+\" mode\");\n    });\n});\n```\n\n### Next.js ≤12 Page\n`index.tsx`\n```typescript\n// Next.js page\nexport default function Home({LANGUAGE_NAMES, TEXT}){\n\n    let components = [];\n    for(let lang in LANGUAGE_NAMES){\n        let name = LANGUAGE_NAMES[lang];\n        components.push(\u003ca href={'/'+lang+'/'}\u003e{name}\u003c/a\u003e);\n    }\n\n    return (\n        \u003c\u003e\n            \u003ch1\u003e{TEXT['TranslationKey']}\u003c/h1\u003e\n            \u003ch2\u003e{TEXT['HelloWorld']}\u003c/h2\u003e\n            {components}\n        \u003c/\u003e\n    );\n}\n\nexport async function getStaticProps(context){\n    const TEXT = await getTranslations(context.locale, context.defaultLocale, [\n        'TranslationKey', 'HelloWorld' \n    ]);\n\n    return {\n        props: {\n            LANGUAGE_NAMES: await getLanguageNames(),\n            TEXT: TEXT\n        }\n    };\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupcode%2Fnode-lup-language","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flupcode%2Fnode-lup-language","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flupcode%2Fnode-lup-language/lists"}