浏览器默认语言与页面访问统计问题二三则
文章目录
- 前言
- 网站默认语言问题
- 网站访问统计问题
- Error: Empty components are self-closing
- Error: A space is required before closing bracket
- 总结
前言
看标题大概能猜到这是一篇杂合体的总结,是这两天处理网站遇到的小问题,怕过段时间再忘了所以总结到一起便于反过来查找问题解决方案,这个问题都是之前未曾接触过的,但遇到他们不用惧怕,只要假装自己能解决就行,这样稳扎稳打的找方案就能解决了。
网站默认语言问题
一个使用Nextjs框架编写的网站,支持中日英三种语言,如果访问主页面时未选择语言,则按照浏览器的语言来选择,这是合理的,一个人如果总用中文的浏览器,那么他在访问一个新网站时较大可能是想看中文的页面,所以我们优先选择当前浏览器的语言即可,默认跳转到指定语言的页面,选择语言的代码如下,是一个中间件 middleware.ts
,问题在于默认语言为日文的浏览器,打开网页是英文的,这就得查查是因为什么了:
import { NextRequest, NextResponse } from 'next/server';
import acceptLanguage from 'accept-language';
import { fallbackLng, languages } from './app/i18n/settings';acceptLanguage.languages(languages);export const config = {// matcher: '/:lng*'matcher: ['/((?!api|_next/static|_next/image|images|svg|assets|favicon.ico|sw.js).*)']
};const cookieName = 'i18next';export function middleware(req: NextRequest) {let lng;// if (req.cookies.has(cookieName)) lng = acceptLanguage.get(req.cookies.get(cookieName)?.value);if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'));if (!lng) lng = fallbackLng;// Redirect if lng in path is not supportedif (!languages.some((loc) => req.nextUrl.pathname.startsWith(`/${loc}`))&& !req.nextUrl.pathname.startsWith('/_next')) {return NextResponse.redirect(new URL(`/${lng}${req.nextUrl.pathname}`, req.url));}if (req.headers.has('referer')) {const refererUrl = new URL(req.headers.get('referer') || 'zh');const lngInReferer = languages.find((l) => refererUrl.pathname.startsWith(`/${l}`));const response = NextResponse.next();if (lngInReferer) response.cookies.set(cookieName, lngInReferer);return response;}return NextResponse.next();
}
其中 matcher
是用来匹配路径的,这个配置表示,中间件会在请求的路径不包含特定字符串时触发。也就是说,路径不能包含指定的 api
、_next/static
等词缀。
网站语言看这一句 if (!lng) lng = acceptLanguage.get(req.headers.get('Accept-Language'));
就行了,它的含义是网站可以根据 Accept-Language 请求头来检测浏览器的首选语言,并返回相应语言的内容。
而我将浏览器默认语言设置为日语时,打开网站传递的 Accept-Language
内容为 ja,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
根据你提供的 Accept-Language
内容 ja,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
,以下是中间件的处理过程:
acceptLanguage.get(req.headers.get('Accept-Language'))
函数会根据 Accept-Language
头部的内容来选择最适合的语言。根据我浏览器传递的内容:
ja
(日语)有最高的优先级。en
(英语)优先级为 0.9。zh-CN
(简体中文)优先级为 0.8。zh
(中文)优先级为 0.7。
由于 ja
的优先级是 1(默认)并且是最优的匹配,所以 acceptLanguage.get()
会选择 ja
作为首选语言。
但是可以还取决于 languages
的值,因为 acceptLanguage
是通过 languages
完成初始化的,而我查询了项目中它的值为 export const languages = ['jp', 'en', 'zh'];
,发现问题了没有?日语浏览器将 ja
作为最优先展示语言,但是备选项里没有 ja
,所以最终选出的结果是 en
,这也就解释了为什么日语浏览器默认打开网页时显示英文页面的现象了。
日文的缩写通常是 “ja”,而不是 “jp”。
- “ja” 是 ISO 639-1 标准中日语的语言代码。
- “jp” 通常用来表示 日本(Japan)的国家代码,在 ISO 3166-1 标准中使用。
因此,在国际化(i18n)和多语言网站的语言代码中,日文通常使用 “ja”。
只要原因后就好修改了,改成ja也好,保持jp也罢,修正逻辑按照一致的顺序处理,问题也就能解决了。
网站访问统计问题
如果是一个网站开发者遇到这种问题,应该会有一个完整的解决方案,而我这种半吊子的网页开发人员,只想尽快的解决问题,所以我的搜索方向就是怎么尽快解决这个问题。起初搜到的方案是使用 Vercel Analytics
来查看,初始有个免费额度,超过25,000 events要进行收费,因为我的网站就部署在 Vercel 上,所以这种方案应该是最融洽的,查看每月的 events 使用量估算了费用,每月要 $100 以上,纠结中。
然后就搜到了 Google Analytics
,介绍里说它完全免费,应用广泛,但是最近评价却不好,一方面是指责它收集隐私信息,另一方面是说它被很多浏览器插件屏蔽,导致无法准确统计,在抵制它的同时很多人都推荐使用了 Umami
。
Umami
是一个开源的简单、隐私友好的网站分析工具。它提供了类似于 Google Analytics 的功能,但更加专注于数据隐私,且无需依赖第三方 cookies。这使得 Umami 成为那些希望追踪用户活动但又需要遵守严格隐私法律(如 GDPR 或 CCPA)的个人和企业的理想选择。
Umami
是完全开源的(使用 MIT 许可证),代码可以在 GitHub 上找到并自行部署。客户端脚本文件仅约 2 KB,加载速度快,不会影响网页性能。时查看访问者活动,例如当前在线人数、浏览器、设备、地域等信息。
后端:Node.js 和 PostgreSQL。前端:基于 React 构建。部署:支持 Docker,配置简单。技术栈提起来挺简单,但是像我这么懒的人现在还不打算配置一遍,自己配置还要买服务器和数据库,因为我查到它有一个 Umami Cloud
。
Umami Cloud 是 Umami
的云托管服务,提供了一个简单、隐私友好的网站分析工具的在线版本。它允许用户通过 Umami 提供的 Web 界面,无需自行托管和管理服务器即可使用该分析工具。相比自己搭建 Umami,使用 Umami Cloud 可以节省大量的部署和维护成本,适合不想处理服务器配置的用户。
用户无需担心部署和维护服务器,Umami Cloud 提供了一个即开即用的服务,只需要注册并配置网站即可开始使用。与自托管的 Umami 一样,Umami Cloud 注重用户隐私,不收集个人可识别信息。它也不依赖 cookies 或第三方追踪,因此完全符合 GDPR 和其他隐私法规。
用户只需要将提供的 JavaScript 跟踪代码插入到网站页面,不需要任何后端代码,适合非技术用户。Umami Cloud 提供了不同的定价套餐,包括免费和付费版本。免费版本适合小型网站或个人使用,付费版本提供更多的功能和数据存储选项。
看起来数据大了还是得付钱,免费版本限制 100K events per month,Up to 3 websites,6 month data retention,对于我的网站来说还是不够用,但付费的价格相比Vercel算是降了不少,所以我把它的 跟踪代码 胡乱的集成到了我项目的 <head>...</head>
中,进而引发了后面的问题
<script defer src="https://cloud.umami.is/script.js" data-website-id="YOUR_WEBSITE_ID"></script>
Error: Empty components are self-closing
这个错误的完整显示如下:
Failed to compile.
30:13 Error: Empty components are self-closing react/self-closing-comp
info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rulesELIFECYCLE Command failed with exit code 1.
Error: Command "pnpm run build" exited with 1
我的代码开始写成了这样:
export default function RootLayout({children,params: { lng },
}: {children: React.ReactNode;params: { lng: LanguageType };
}) {return (<html lang={lng} className={myFont.variable}><head><script defer src="https://cloud.umami.is/script.js" data-website-id="YOUR_WEBSITE_ID"></script></head><body className="relative font-my">{children}</body></html>);
}
看着ChatGPT的解释我陷入了沉思,不过后来我明白了它的意思,这个错误是由于 ESLint 中的 react/self-closing-comp
规则引起的,该规则要求空的组件标签必须是自闭合的。
解决方法是检查报错所在的代码(通常是在第 30 行第 13 列),很可能是你有一个空的元素,例如:
<div></div>
它应该改成自闭合的写法:
<div />
所以我把代码改成了这样 <head><script defer src="https://cloud.umami.is/script.js" data-website-id="YOUR_WEBSITE_ID"/></head>
,去掉了 </script>
然后就报了下面这个错。
Error: A space is required before closing bracket
完整错误如下:
Failed to compile.
30:120 Error: A space is required before closing bracket react/jsx-tag-spacing
info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rulesELIFECYCLE Command failed with exit code 1.
Error: Command "pnpm run build" exited with 1
这次我还是没反应过来,经过ChatGPT解释发现,之前就已经给出准备的例子了,这个新的错误是由 ESLint 的 react/jsx-tag-spacing 规则引起的,它要求在自闭合标签的闭合括号前留一个空格。所以最终改成下面这样就没问题了
<head><script defer src="https://cloud.umami.is/script.js" data-website-id="YOUR_WEBSITE_ID" /></head>
真是一个空格也不能错啊~
总结
- 网站的默认语言可以根据浏览器的设置语言来初始化,获取代码简化为
acceptLanguage.get(req.headers.get('Accept-Language'));
- 网页统计可以使用
Vercel Analytics
、Google Analytics
或者Umami
Vercel Analytics
与 Vercel 兼容性好,毕竟出自同一家班底,配合会比较顺畅,应该还会有自家产品的关联优化Google Analytics
依赖 cookies,可能涉及隐私问题,数据存储在 Google 的服务器,由 Google 管理,功能全面,适合复杂需求Umami
完全隐私友好,无 cookies,核心功能足够,但比 Google Analytics 少,可以用户自行托管,完全掌控
他日若是同淋雪,也算此生共白头。在两个人维系的关系中,一方的忍耐退让并不会让另一方生出善意,反而会导致对方以为之前的所有都是理所应当,进而变本加厉。总会有两个人不在一个频道上,你给她讲理,她跟你讲情,你跟她讲情,她跟你讲法,你跟她讲法,他跟你讲理,反正你永远对不了~
相关文章:

浏览器默认语言与页面访问统计问题二三则
文章目录 前言网站默认语言问题网站访问统计问题Error: Empty components are self-closingError: A space is required before closing bracket 总结 前言 看标题大概能猜到这是一篇杂合体的总结,是这两天处理网站遇到的小问题,怕过段时间再忘了所以总…...

用Python绘制一只懒羊羊
目录 一、准备工作 二、Turtle库简介 三、绘制懒羊羊的步骤 1. 导入Turtle库并设置画布 2. 绘制头部 3. 绘制眼睛 4. 绘制嘴巴 5. 绘制身体 6. 绘制四肢 7. 完成绘制 五、运行代码与结果展示 六、总结 在这个趣味盎然的技术实践中,我们将使用Python和Turtle图形…...

虹科分享 | 汽车NVH小课堂之听音辨故障
随着车主开始关注汽车抖动异响问题,如何根据故障现象快速诊断异响来源,成了汽修人的必修课。 一个比较常用的方法就是靠“听”——“听音辨故障”。那今天,虹科Pico也整理了几个不同类型的异响声音,一起来听听看你能答对几个吧 汽…...

论文速读|SigLIP:Sigmoid Loss for Language Image Pre-Training.ICCV23
论文地址:https://arxiv.org/abs/2303.15343v4 代码地址:https://github.com/google-research/big_vision bib引用: misc{zhai2023sigmoidlosslanguageimage,title{Sigmoid Loss for Language Image Pre-Training}, author{Xiaohua Zhai and…...

深度学习笔记——循环神经网络之LSTM
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍面试过程中可能遇到的循环神经网络LSTM知识点。 文章目录 文本特征提取的方法1. 基础方法1.1 词袋模型(Bag of Words, BOW)工作…...

算法整理:2-opt求解旅行商(Python代码)
文章目录 算法思想算法步骤代码1纯函数代码2纯函数数据可视化 算法思想 通过交换边进行寻优。 算法步骤 把初始解作为当前解 通过交换边生成新解 如果新解优于历史最优解,则更新当前解为新解 重复2,3,直到当前解交换了所有的边均不能改…...

状态模式
在软件开发过程中,我们经常会遇到这样的情况:一个对象的行为会随着其内部状态的改变而发生变化。例如,一个手机在不同状态下(开机、关机、静音等)对相同的操作(如来电)会有不同的反应。传统的解…...

RoHS 简介
RoHS(Restriction of Hazardous Substances Directive,限制有害物质指令)是欧盟制定的一项环保法规,旨在限制电气和电子设备中某些有害物质的使用,以减少这些产品对环境和人体健康的危害。 RoHS限制的有害物质及其限量…...

【Vim Masterclass 笔记26】S11L46:Vim 插件的安装、使用与日常管理
文章目录 Section 11:Vim PluginsS11L46 Managing Vim Plugins1 第三方插件管理工具2 安装插件使用的搜索引擎3 Vim 插件的安装方法4 存放 Vim 插件包的路径格式5 示例一:插件 NERDTree 的安装6 示例二:插件 ctrlp.vim 的安装7 示例三&#x…...

深度学习原理与Pytorch实战
深度学习原理与Pytorch实战 第2版 强化学习人工智能神经网络书籍 python动手学深度学习框架书 TransformerBERT图神经网络: 技术讲解 编辑推荐 1.基于PyTorch新版本,涵盖深度学习基础知识和前沿技术,由浅入深,通俗易懂…...

ELK环境搭建
文章目录 1.ElasticSearch安装1.安装的版本选择1.SpringBoot版本:2.4.2 找到依赖的spring-data-elasticsearch的版本2.spring-data-elasticsearch版本:4.1.3 找到依赖的elasticsearch版本3.elasticsearch版本:7.9.3 2.安装1.官方文档2.下载压…...

基于Springboot + vue实现的民俗网
“前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:人工智能学习网站” 💖学习知识需费心, 📕整理归纳更费神。 🎉源码免费人人喜…...

第24篇 基于ARM A9处理器用汇编语言实现中断<六>
Q:怎样设计ARM处理器汇编语言程序使用定时器中断实现实时时钟? A:此前我们曾使用轮询定时器I/O的方式实现实时时钟,而在本实验中将采用定时器中断的方式。新增第三个中断源A9 Private Timer,对该定时器进行配置&#…...

【数据结构】_不带头非循环单向链表
目录 1. 链表的概念及结构 2. 链表的分类 3. 单链表的实现 3.1 SList.h头文件 3.2 SList.c源文件 3.3 Test_SList.c测试文件 关于线性表,已介绍顺序表,详见下文: 【数据结构】_顺序表-CSDN博客 本文介绍链表; 基于顺序表…...

golang 使用双向链表作为container/heap的载体
MyHeap:container/heap的数据载体,需要实现以下方法: Len:堆中数据个数 Less:第i个元素 是否必 第j个元素 值小 Swap:交换第i个元素和 第j个元素 Push:向堆中追加元素 Pop:从堆…...

C#集合操作优化:高效实现批量添加与删除
在C#中,对集合进行批量操作(如批量添加或删除元素)通常涉及使用集合类型提供的方法和特性,以及可能的循环或LINQ查询来高效地处理大量数据。以下是一些常见的方法和技巧: 批量添加元素 使用集合的AddRange方法&#x…...

142.WEB渗透测试-信息收集-小程序、app(13)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:141.WEB渗透测试-信息收集-小程序、app(12) 软件用法,…...

24.日常算法
1. 数组中两元素的最大乘积 题目来源 给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。请你计算并返回该式的最大值。 示例 1: 输入:nums [3,4,5,2] 输出:12 解释…...

分布式理解
分布式 如何理解分布式 狭义的分布是指,指多台PC在地理位置上分布在不同的地方。 分布式系统 分布式系**统:**多个能独立运行的计算机(称为结点)组成。各个结点利用计算机网络进行信息传递,从而实现共同的“目标或者任…...

wordpress调用指定ID页面的链接
在WordPress中,如果你想调用一个指定ID的页面链接,可以使用以下几种方法: 方法一:使用页面ID 你可以直接使用页面的ID来生成链接。例如,如果你想链接到ID为123的页面,可以使用以下代码: <…...

单值二叉树(C语言详解版)
一、摘要 今天要讲的是leetcode单值二叉树,这里用到的C语言,主要提供的是思路,大家看了我的思路之后可以点击链接自己试一下。 二、题目简介 如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。 只有给定的树是单…...

python学opencv|读取图像(四十二)使用cv2.add()函数实现多图像叠加
【1】引言 前序学习过程中,掌握了灰度图像和彩色图像的掩模操作: python学opencv|读取图像(九)用numpy创建黑白相间灰度图_numpy生成全黑图片-CSDN博客 python学opencv|读取图像(四十)掩模:三…...

速通Docker === Docker Compose
目录 Docker Compose 简介 Docker Compose 常用命令 使用 Docker Compose 启动 WordPress 普通启动方式(使用 Docker 命令) 使用 Docker Compose 启动 Docker Compose 的特性 Docker Compose 简介 Docker Compose 是一个用于定义和运行多容器 Dock…...

LMI Gocator GO_SDK VS2019引用配置
LMI SDK在VS2019中的引用是真的坑爹,总结一下经验,希望后来的人能少走弯路.大致内容如下: (1) 环境变量 (2)C/C 附加包含目录 E:\GWQ\Gocator\GO_SDK\Gocator\GoSdk E:\GWQ\Gocator\GO_SDK\Platform\kApi (3&#…...

技术之翼,创作之心
引言:初入编程的迷茫与追求 当我第一次接触到编程时,心中充满了既期待又迷茫的情感。那时,我还是一名刚刚踏入大学的学生,面对一门陌生而复杂的学科——计算机科学,我的内心充满了好奇与困惑。课堂上,老师…...

WebSocket异步导出
WebSocket异步导出 1、安装sockjs-client和stompjs2、连接后台3、vite.config.ts 配置反向代理4、导出并实时通信5、 封装WebSocket 文件注册登录(城通网盘) 1、安装sockjs-client和stompjs import SockJS from sockjs-client/dist/sockjs.min.js import Stomp from stompjs2、…...

OS2.【Linux】基本命令入门(1)
目录 1.操作系统是什么? 2.好操作系统的衡量标准 3.操作系统的核心工作 4.在计算机上所有行为都会被转换为硬件行为 5.文件 6.简单介绍一些基本命令 1.clear 2.pwd 3.ls 1.ls -l 2.隐藏文件的创建 3.ls -al 4.ls -ld 5.ls -F(注意是大写) 4.cd 1.cd .. "…...

【二叉树】4. 判断一颗二叉树是否是平衡二叉树。5. 对称二叉树。6. 二叉树的构建及遍历 7. 二叉树的分层遍历 。
判断一颗二叉树是否是平衡二叉树。OJ链接 可以在求树高度的过程中判断树是否平衡 对称二叉树。OJ链接 二叉树的构建及遍历。OJ链接 注意:public static int i最好把static去掉 否则当有多个测试用例时 i无法重新为0二叉树的分层遍历 。OJ链接 但此题要求返回List…...

OS Copilot功能测评:智能助手的炫彩魔法
简介: OS Copilot 是一款融合了人工智能技术的智能助手,专为Linux系统设计,旨在提升系统管理和运维效率。本文详细介绍了在阿里云ECS实例上安装和体验OS Copilot的过程,重点评测了其三个核心参数:-t(模式…...

MFC结构体数据文件读写实例
程序功能将结构体内数组数据写入文件和读出 2Dlg.h中代码: typedef struct Student {int nNum[1000];float fScore;CString sss;}stu; class CMy2Dlg : public CDialog { // Construction public:CMy2Dlg(CWnd* pParent NULL); // standard constructorstu stu1; ... } 2Dl…...