前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入Go:Internationalization-国际化

深入Go:Internationalization-国际化

作者头像
wenxing
发布于 2021-12-14 03:23:05
发布于 2021-12-14 03:23:05
1.1K00
代码可运行
举报
文章被收录于专栏:Frames of WenxingFrames of Wenxing
运行总次数:0
代码可运行

当服务需要应对多语言场景时,我们应该如何组织代码?

说明

本文不探讨诸如单复数变换等复杂情况,如有需要,请参见这里

(太长不看版)
  1. 读取语言标签与相应翻译,进行翻译字符串的注册
  2. 获取时根据语言解析标签并获得相应语言的Printer,根据Key进行翻译字符串的查找与生成

示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
  "golang.org/x/text/language"
  "golang.org/x/text/message"
)

// 此处`und`为undefined,用于语言无对应的翻译时的显示
// 请根据翻译文件准备好相应的key与翻译字符串
var msg = map[string]string{"zh": "你好,%s", "en": "Hello, %s", "und": "Hello, %s"}
var key = "HelloString" // key是字典用于查找翻译字符串的键;当然这里也可以使用"Hello, %s"

func register(k string, d map[string]string) {
  for lang, translation := range d {
    // 根据语言字符串解析语言标签
    tag, err := language.Parse(lang)
    if err != nil {
      // 语言注册建议在程序初始化阶段完成,此时出错可能直接panic较好
      panic(err)
    }
    // 根据语言tag、key与翻译字符串进行设置
    err = message.SetString(tag, key, translation)
    if err != nil {
      panic(err)
    }
  }
}

func getTranslation(k, lang string, content []interface{}) string {
  tag, err := language.Parse(lang)
  if err != nil {
    // 指定解析出错时希望返回的语言
    tag = language.Und
  }
  // 根据语言获取Printer
  p := message.NewPrinter(tag)
  return p.Sprintf(k, content...)
}

func main() {
  register(key, msg)
  println(getTranslation(key, "en", []interface{}{"en"}))
  println(getTranslation(key, "en-US", []interface{}{"en-US"}))
  println(getTranslation(key, "zh", []interface{}{"zh"}))
  // zh-CN 和 zh-SG 的 parent 都是 zh,因此会根据 zh 进行返回
  println(getTranslation(key, "zh-CN", []interface{}{"zh-CN"}))
  println(getTranslation(key, "zh-SG", []interface{}{"zh-SG"}))
  // zh-TW 的 parent 并不是 zh,因此会根据 und tag 返回相应的翻译
  println(getTranslation(key, "zh-TW", []interface{}{"zh-TW"}))
  // 解析语言出错时的处理
  println(getTranslation(key, "???", []interface{}{"???"}))
}
/* 输出:
Hello, en
Hello, en-US
你好,zh
你好,zh-CN
你好,zh-SG
Hello, zh-TW
Hello, ???
*/
(太长不看版结束)

Prerequisites: 原理

语言标签

我们通常可以在HTTP header里看见类似于Accept-Language: zh-CN或是Accept-Language: en之类的值,这里header里对应的值就是语言标签。类似地,zh-cmn-Hans-CN也是语言标签。

语言标签的语法

我们需要关注的语言标签的语法:

主语言子标签-扩展语言子标签-文字子标签-地区子标签

zh-cmn-Hans-CN

  • 除了主语言子标签是必填,其他都是可选;
  • 扩展语言子标签为3字母,最多可有三个;
  • 文字子标签为4字母(首字母大写),最多一个;
  • 地区子标签为2字母(通常大写)

因此,zh-cmn-Hans-CN被解读为:

汉语(zhongwen)-普通话(simplified mandarin)-简体(Han Simplified)-中国大陆

另外,该语言标签在2009年后就不再被推荐使用了,因为扩展语言标签cmn蕴含该语言是zh(汉语)。当然,目前为了兼容,建议使用zh而非cmn

Go存储翻译字符串

Go通过调用message.SetString(tag language.Tag, key string, msg string)来存储翻译字符串;其中,tag为语言标签,key为该字符串的键,msg为该字符串在该语言下的值。例如

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// tag: en
message.SetString(language.English, "HelloString", "Hello, %s")
// tag: zh-Hans
message.SetString(language.SimplifiedChinese, "HelloString", "你好,%s") 
// tag: und
message.SetString(language.Und, "HelloString", "Hi, %s")

Go获取翻译字符串

Go通过调用message.NewPrinter(tag language.Tag)来获取翻译字符串字典,通过调用printer.Sprintf(key string, content ...interface{})(或其他类似于fmt中的方法)来生成(或打印)翻译字符串。下文我们统一使用Sprintf作为示例。

打印的时候,使用key并根据语言标签查找相应的字典,如果在该语言标签中找不到该key,则依次在其祖先节点中继续查找;如果找到根节点(und)仍未找到,则效果同直接调用fmt.Sprintf相同。因此,在简单场景下,建议直接在enzhund下增加翻译语句und用于处理无法解析的语言标签(例如,???)或意料之外的语言标签(例如,zh-TW的祖先节点依次为zh-Hantund)。

例如

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
message.NewPrinter(language.English, "en") // Hello, en
message.NewPrinter(language.AmericanEnglish, "en-US") //* Hello, en-US
message.NewPrinter(language.Chinese, "zh") //** Hi, zh
message.NewPrinter(language.SimplifiedChinese, "zh-Hans") // 你好,zh-Hans
/*
*   这里,因为en-US的父节点为en,因此可以找到对应翻译字符串
**  这里,因为zh的父节点是und,因此找到了und内的翻译字符串(zh-Hans是zh的子节点)
*/

实践

Step 1: 准备字典

建议将各语言的翻译字符串与语言标签准备在单独的文本文件里,通过读取文件、解析标签与字符串,调用message.SetString设置。

Step 2: 根据传入的语言标签获取翻译字符串

根据语言标签调用language.Parse获取tag,使用该tag获取message.Printer,使用该printer根据key调用printer.Sprintf()生成翻译字符串。

gRPC Gateway获取请求中的语言标签

gRPC Gateway会将HTTP headers存储在context中,调用metadata.FromIncomingContext(ctx context.Context)可获取到。

注意,获取到的是map[string][]string的结构,map的键为HTTP headers的各名称,因此可能是全小写(accept-language)也可能是首字母大写(Accept-Language),因此需要使用strings.EqualFold(k, "accept-language")来忽略大小写地比较。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年 05月11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
CentOS 7 部署 OpenVPN
环境: 外网IP:139.198.15.121 内网IP:10.180.27.8
陳斯托洛夫斯記
2022/10/27
8.1K1
CentOS 7 部署 OpenVPN
企业级openvpn搭建
easy-rsa 下载地址:https://codeload.github.com/OpenVPN/easy-rsa-old/zip/master
章工运维
2023/05/19
5.5K0
ubuntu部署VPN中openvpn(上)
如果在一个非信任网络下比如旅社或者咖啡店的WiFi网络下,想要通过你的智能手机或者笔记本电脑安全地访问互联网,那么VPN可以满足你的要求。VPN(VirtualPrivate Network)允许你私有地(privately)安全地(securely)穿过非信任的网络,就好像为你建立了一条专属网络。你的数据流量到达VPN服务器之后,VPN服务器继续将你的网络流量送达目的地。
陈不成i
2021/06/30
15.6K0
CentOS 搭建 OpenVPN 服务,一次性成功!收藏了
本篇文章包含OpenVPN应用场景,OpenVPN服务端搭建,OpenVPN客户端搭建(windows+linux),OpenVPN密码认证,手把手教大家搭建OpenVPN!
民工哥
2023/08/22
33.1K2
CentOS 搭建 OpenVPN 服务,一次性成功!收藏了
Centos7安装与配置OpenVPN服务器
安装 OpenVPN、Firewalld 软件包以及用于生成各种证书的 EasyRSA
用户7639835
2021/09/01
17.9K4
[经验分享] 腾讯云ubuntu搭建openvpn
./build-dh 命令完成后,我们会/usr/share/doc/openvpn/examples/easy-rsa/2.0/keys 得到 ca.crt、ca.key、dh1024.pem 等文件。其中,ca.key 的安全非常重要,OpenVPN 并不需要这个文件,所以可以存放在其他比较安全的地方,否则,OpenVPN 的通信将不再安全。 cp /usr/share/doc/openvpn/examples/easy-rsa/2.0/keys/server.crt /etc/openvpn/ cp /usr/share/doc/openvpn/examples/easy-rsa/2.0/keys/server.key /etc/openvpn/ cp /usr/share/doc/openvpn/examples/easy-rsa/2.0/keys/dh1024.pem /etc/openvpn/ cp /usr/share/doc/openvpn/examples/easy-rsa/2.0/keys/ca.crt /etc/openvpn/ 生成服务端证书 ./build-key-server server
知识与交流
2023/03/25
12.5K0
[经验分享] 腾讯云ubuntu搭建openvpn
使用Lighthouse搭建OpenVPN轻松访问内网服务
OpenVPN是一种功能强大的开源虚拟私有网络(VPN)解决方案,可以在多种应用场景下使用。以下是几个常见的OpenVPN应用场景:
小宇-xiaoyu
2023/11/28
3.1K0
OpenVPN原理及部署使用
简介 VPN技术通过密钥交换、封装、认证、加密手段在公共网络上建立起私密的隧道,保障传输数据的完整性、私密性和有效性。OpenVPN是近年来新出现的开放源码项目,实现了SSL VPN的一种解决方案。 传统SSL VPN通过端口代理的方法实现,代理服务器根据应用协议的类型(如http,telnet等)做相应的端口代理,客户端与代理服务器之间建立SSL安全连接,客户端与应用服务器之间的所有数据传输通过代理服务器转发。这种实现方式烦琐,应用范围也比较窄:仅适用于用TCP固定端口进行通信的应用系统,且对每个需要
iginkgo18
2020/09/27
48.6K1
OpenVPN原理及部署使用
OpenV**集成LDAP踩坑记
openv** 版本:2.4.9-1.el7 ease-rsa 版本:3.0.7-1.el7 openv**-auth-ldap 版本:2.0.3-17.el7
cuijianzhe
2022/06/14
1.2K1
OpenV**集成LDAP踩坑记
OpenVPN server端配置文件详细说明
本文将介绍如何配置OpenVPN服务器端的配置文件。在Windows系统中,该配置文件一般叫做server.ovpn;在Linux/BSD系统中,该配置文件一般叫做server.conf。虽然配置文件名称不同,但其中的配置内容与配置方法却是相同的。
用户2135432
2023/10/21
11.6K0
基于 WireGuard 和 OpenVPN 的混合云基础架构建设
可以找一台能联网的 centos7 测试一下这个端口,如果没有 nc 工具可以yum install nc安装下。:
米开朗基杨
2021/04/23
8.1K1
基于 WireGuard 和 OpenVPN 的混合云基础架构建设
centos7部署OpenVpn
VPN直译就是虚拟专用通道,是提供给企业之间或者个人与公司之间安全数据传输的隧道,OpenVPN无疑是Linux下开源VPN的先锋,提供了良好的性能和友好的用户GUI。
Lucifer三思而后行
2021/08/17
9.6K1
centos7部署OpenVpn
ubuntu部署VPN中openvpn(下)
1.创建客户端目录,存储客户端文件 mkdir -p ~/client-configs/files
陈不成i
2021/06/30
4.6K0
运维实战技能Get---OpenV**搭建.
作为运维人员经常会遇到各种故障,那我们就需要远程解决,为了保障服务器的安全性,此时我们需要连接内网,然后连接到跳板机,openvpn是我们常用解决方案!下面我们介绍一下在云服务器搭建openvpn方法!
用户5766185
2019/07/08
4.7K1
运维实战技能Get---OpenV**搭建.
openvpn安装及证书制作
1) 安装epel 仓库源 wget http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm rpm -Uvh epel-release-6-8.noarch.rpm
一朵灼灼华
2022/08/05
8.3K0
openvpn安装及证书制作
centos7上搭建openv**centos7 上搭建openv**
基于内外网隔离 网络审计 又因为最近疫情反复,很多小公司没有做好内外网网络隔离,也不能够远程办公。本文基于开源免费的openv**为大家提供一个参考。
怀朔
2022/05/29
9110
VPN技术指南:OpenVPN和IPsec的配置与管理
这里推荐一篇实用的文章:《小型项目架构设计:提升可维护性与扩展性的实用原则》,作者:【不惑】。
Echo_Wish
2024/12/03
4490
VPN技术指南:OpenVPN和IPsec的配置与管理
open魏屁嗯-安装部署
1、场景,由于同事在家需要远程支撑工作,但是公司的服务只对公司的公网宽带开了白名单,所以同志们在家无法访问,需要拨号到公司的公网地址才可以访问。
@凌晨
2023/03/11
1.6K0
open魏屁嗯-安装部署
OpenWRT搭建OpenVPN服务器
By HKL, on Thursday 2019-11-28 23:21, tagged: 🏷️Networking 🏷️Operating
hiplon
2023/10/18
17.2K1
OpenWRT搭建OpenVPN服务器
VPN技术指南:OpenVPN和IPsec的配置与管理
在现代网络安全中,虚拟专用网络(VPN)是保护数据传输安全、实现远程访问的关键技术。OpenVPN和IPsec是两种广泛应用的VPN解决方案,各具优势。本文将详细介绍如何配置和管理OpenVPN和IPsec,并提供相关代码和示例,帮助读者理解和应用这些技术。
Echo_Wish
2024/12/04
8570
相关推荐
CentOS 7 部署 OpenVPN
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档