Angular 7 Universal多语言SSR导航器检测致谷歌搜索语言异常
解决Angular 7 Universal多语言SSR的谷歌SEO问题
问题根源分析
你当前的方案核心问题在于:navigator.language是浏览器端专属API,SSR在服务器运行时根本访问不到这个值。这就导致服务器初始渲染的页面没有正确的语言上下文,之后前端跳转才设置语言、更新SEO信息——谷歌爬虫可能会抓取到初始渲染的默认语言内容和跳转后的内容混合,最终出现标题和描述不匹配的异常。
优化方案:将浏览器语言传递到服务器完成SSR渲染
要解决这个问题,关键是让服务器在SSR阶段就能拿到用户的浏览器语言,直接渲染对应语言的页面,而不是依赖前端跳转。具体步骤如下:
1. 服务器端从请求头提取用户首选语言
在Angular Universal的server.ts文件中,解析请求的Accept-Language头(浏览器会自动发送这个头,包含用户的语言偏好),提取优先级最高的支持语言:
import { Request } from 'express'; // 解析Accept-Language头,返回首选的支持语言 function getPreferredLanguage(req: Request): string { const acceptLanguage = req.headers['accept-language']; if (!acceptLanguage) return 'en'; // 设置默认语言 // 解析语言列表并按优先级排序 const languageList = acceptLanguage.split(',').map(item => { const [code, qStr] = item.trim().split(';'); const priority = qStr ? parseFloat(qStr.split('=')[1]) : 1; return { code: code.split('-')[0], priority }; }).sort((a, b) => b.priority - a.priority); // 匹配你的支持语言列表 const supportedLangs = ['en', 'es']; return languageList.find(lang => supportedLangs.includes(lang.code))?.code || 'en'; }
2. 将语言传递给Angular应用
在server.ts的渲染逻辑中,把提取到的语言作为依赖注入到Angular应用里:
// 先定义一个注入令牌 export const REQUEST_LANGUAGE = new InjectionToken<string>('REQUEST_LANGUAGE'); // 在渲染页面时提供这个令牌 res.render(indexHtml, { req, res, providers: [ { provide: REQUEST_LANGUAGE, useValue: getPreferredLanguage(req) } ] });
3. 在应用中优先使用服务器传递的语言
修改language.component.ts,区分服务器端和客户端的语言获取逻辑:
import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformServer, isPlatformBrowser } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { Router } from '@angular/router'; import { REQUEST_LANGUAGE } from '../server.tokens'; // 导入令牌 import { SeoService } from './seo.service'; @Component({ selector: 'app-language', templateUrl: './language.component.html' }) export class LanguageComponent implements OnInit { constructor( private translate: TranslateService, private router: Router, private seoService: SeoService, @Inject(REQUEST_LANGUAGE) private serverLang: string, @Inject(PLATFORM_ID) private platformId: Object ) {} ngOnInit() { let targetLang: string; // 服务器端:直接用请求头获取的语言 if (isPlatformServer(this.platformId)) { targetLang = this.serverLang; } // 客户端:优先用用户之前保存的语言,否则用浏览器语言 else { const savedLang = localStorage.getItem('selectedLang'); targetLang = savedLang || navigator.language.split('-')[0]; } // 确保语言在支持列表内,否则用默认 targetLang = ['en', 'es'].includes(targetLang) ? targetLang : 'en'; // 如果当前路由没有语言前缀,跳转到对应语言路由 const currentUrl = this.router.url; if (!currentUrl.startsWith(`/${targetLang}`)) { this.router.navigate([`/${targetLang}`]); } else { // 设置翻译服务和SEO信息 this.translate.use(targetLang); this.seoService.setSeoData(targetLang); } } }
4. 优化路由与SEO设置
确保每个语言路由都是独立的(比如/en/home、/es/home),并且在SeoService中设置HTML的lang标签时,直接使用当前路由的语言前缀或者已设置的TranslateService语言,保证SSR渲染时就能输出正确的lang属性、标题和元描述。
额外优化建议
- 给用户提供手动切换语言的按钮,切换后将选择的语言保存到
localStorage,下次客户端加载时优先使用这个值。 - 在页面头部添加
<link rel="alternate" hreflang="es" href="https://yourdomain/es/home" />这类标签,帮助谷歌识别不同语言的页面版本,提升多语言SEO效果。
内容的提问来源于stack exchange,提问作者Carlos Torrecillas
相关产品推荐
相关产品推荐

