ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Go Web 应用国际化(i18n)完整方案小结:从 Locale 设置到 go-i18n 多语言站点

Go Web 应用国际化(i18n)完整方案小结:从 Locale 设置到 go-i18n 多语言站点 文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本章第 10 章《国际化和本地化》围绕 i18n 的三大核心问题——如何确定 locale、如何保存 locale 相关的资源、如何根据 locale 提取对应内容——给出了一个完整的 Go Web 国际化方案并沉淀为开源库 go-i18n。读完本文你将系统掌握为 Go Web 应用添加多语言支持的完整路径从设置默认地区、本地化文本/时间/货币/视图资源到用 go-i18n 管理多语言包并在模板中直接完成翻译从而做到只增加语言包、不改应用代码即可支持新语言。本文是 10.4 小结 的展开正文内容以第 10 章各小节设置默认地区、本地化资源、国际化站点为主体骨架并结合 第 14.5 节多语言支持 的框架集成实现加以佐证。一、本章脉络i18n 是什么、要解决哪三个问题国际化和本地化Internationalization and Localization通常用 i18n 和 L10N 表示国际化是将针对某个地区设计的程序进行重构使它在更多地区可用本地化则是在面向国际化的程序中增加对新地区的支持。目标很明确——同样的页面在不同语言环境下显示不同效果应用在运行时根据请求来源的地域与语言呈现不同的用户界面参见 10.0 章概述。在 Go 标准库不提供 i18n 支持的背景下要构建一个完整的 i18n 方案必须依次解决三个问题如何确定 locale如何保存与 locale 相关的字符串或其它信息文本、时间日期、货币、图片、视图等如何根据 locale 提取字符串和其它相应的信息。本章按此顺序组织为三小节第一小节解决如何设置正确的 locale第二小节解决如何处理/存储与 locale 相关的资源第三小节解决如何根据不同 locale 返回合适内容。三小节合起来就是一个可落地、可扩展的 Go i18n 方案。二、核心回顾一如何确定 Locale设置默认地区Locale 的命名规范Locale 是一组描述世界上某一特定区域文本格式和语言习惯的设置的集合其名称通常由三部分组成第一部分强制语言缩写如en英文、zh中文第二部分可选下划线后的国家说明符用于区分讲同一种语言的不同国家如en_US美国英语、en_UK英国英语第三部分可选句点后的字符集说明符如zh_CN.gb2312中国、gb2312 字符集。Go 语言默认采用 UTF-8 编码集因此本章实现 i18n 时不考虑第三部分统一采用前两部分作为 i18n 标准的 locale 名。实用提示在 Linux 和 Solaris 系统中可用locale -a命令列举所有支持的地区名观察其命名规范BSD 等系统没有 locale 命令地区信息存储在/usr/share/locale中。方式一通过域名设置 Locale应用运行时按域名分级映射 locale例如把www.asta.com作为英文站默认站、www.asta.cn作为中文站。整域名映射只需一个 map 或简单的判断逻辑if r.Host www.asta.com { i18n.SetLocale(en) } else if r.Host www.asta.cn { i18n.SetLocale(zh-CN) } else if r.Host www.asta.tw { i18n.SetLocale(zh-TW) }也可以按子域名切分设置如en.asta.com表示英文站点、cn.asta.com表示中文站点prefix : strings.Split(r.Host, .) if prefix[0] en { i18n.SetLocale(en) } else if prefix[0] cn { i18n.SetLocale(zh-CN) } else if prefix[0] tw { i18n.SetLocale(zh-TW) }域名方式的优点是URL 一眼可辨、用户直观知道访问哪种语言站点、Go 中用 map 即可实现、有利于搜索引擎抓取SEO。缺点是域名成本高每种语言一个域名且统一名称的域名不一定申请得到且不愿为每个站点单独本地化配置所以实践中更多采用 URL 带参数的方式。方式二通过 URL 参数设置 Locale最常用的做法是在 URL 里带参数例如www.asta.com/hello?localezh或www.asta.com/zh/hello然后设置地区i18n.SetLocale(params[locale])。这种方式几乎拥有域名方式的全部优点且采用 RESTful 风格、无需额外处理方法代价是每个链接里都要携带locale参数略显繁琐。缓解办法是写一个通用的 URL 生成函数所有链接地址都经由该函数生成并在函数内自动追加localeparams[locale]。若希望 URL 更 RESTful、更利于 SEO如www.asta.com/en/books、www.asta.com/zh/books可通过 router 直接提取 locale 路径段mux.Get(/:locale/books, listbook)方式三通过客户端信息设置 Locale一些场景需要根据客户端信息而非 URL来设置 locale主要来源有三类Accept-Language 请求头客户端请求时在 HTTP 头携带Accept-Language据此设置地区AL : r.Header.Get(Accept-Language) if AL en { i18n.SetLocale(en) } else if AL zh-CN { i18n.SetLocale(zh-CN) } else if AL zh-TW { i18n.SetLocale(zh-TW) }实际应用中可能需要更严格的判断逻辑如按权重解析多语言列表。IP 地址根据 IP 库把访问 IP 映射到国家地区再据此设置 locale例如当时常用的 GeoIP Lite Country 库机制简单查库 → 得到国家/地区 → 设置对应 locale。用户 profile让用户通过下拉菜单等方式主动选择 locale将选择保存到其账号 profile 中用户再次登录时把该设置复写回 locale保证该用户每次访问都基于自己先前选择的语言。小结设置 locale 有多种方式应按需求选择让用户以最熟悉的方式获得服务、提升友好性。三、核心回顾二如何保存与 Locale 相关的资源本地化资源确定 locale 后下一步是存储与 locale 对应的信息文本、时间日期、货币值、图片、包含文件及视图等。本章将这些格式信息存储在 JSON 中再按需取出展示下文以中文、英文两种语言对比存储文件为en.json与zh-CN.json。本地化文本消息文本是本地化资源中数量最多、最常用的一类。最简单的方案是用map[string]map[string]string维护 key-value 关系输出前从合适的 map 中取文本package main import fmt var locales map[string]map[string]string func main() { locales make(map[string]map[string]string, 2) en : make(map[string]string, 10) en[pea] pea en[bean] bean locales[en] en cn : make(map[string]string, 10) cn[pea] 豌豆 cn[bean] 毛豆 locales[zh-CN] cn lang : zh-CN fmt.Println(msg(lang, pea)) fmt.Println(msg(lang, bean)) } func msg(locale, key string) string { if v, ok : locales[locale]; ok { if v2, ok : v[key]; ok { return v2 } } return }把lang切换为en即可切换到英文。当文本中包含变量如I am 30 years old对应中文我今年30岁了时可结合fmt.Printf实现带参数的翻译en[how old] I am %d years old cn[how old] 我今年%d岁了 fmt.Printf(msg(lang, how old), 30)以上仅演示内部实现思路实际数据存储在 JSON 中通过json.Unmarshal填充到对应 map。本地化日期和时间同一时刻在不同时区、不同 locale 下时间表示与格式都不同例如中文环境可能显示2012年10月24日 星期三 23时11分13秒 CST英文环境可能显示Wed Oct 24 23:11:13 CST 2012。需要解决两个问题时区问题与格式问题。时区方面$GOROOT/lib/time包中的timeinfo.zip含有 locale 对应的时区定义。先通过time.LoadLocation(name string)获取对应时区如Asia/Shanghai、America/Chicago再结合time.Now()得到本地化时间en[time_zone] America/Chicago cn[time_zone] Asia/Shanghai loc, _ : time.LoadLocation(msg(lang, time_zone)) t : time.Now() t t.In(loc) fmt.Println(t.Format(time.RFC3339))格式方面与文本处理方式类似把格式串作为 key-value 存储再自行实现%Y %m %d %H %M %S等占位符替换en[date_format] %Y-%m-%d %H:%M:%S cn[date_format] %Y年%m月%d日 %H时%M分%S秒 fmt.Println(date(msg(lang, date_format), t)) func date(format string, t time.Time) string { year, month, day : t.Date() hour, min, sec : t.Clock() // 解析相应的 %Y %m %d %H %M %S 然后返回信息 // %Y 替换成 2012 // %m 替换成 10 // %d 替换成 24 }本地化货币值各地区货币表示不同处理方式与日期类似——把格式串存入 key-value再用fmt.Sprintf代入金额en[money] USD %d cn[money] %d元 fmt.Println(money_format(msg(lang, money), 100)) func money_format(format string, money int64) string { return fmt.Sprintf(format, money) }本地化视图和资源不同 locale 可能需要展示不同视图及图片、css、js 等静态资源。做法是按 locale 组织文件目录views |--en //英文模板 | |--images //存储图片信息 | |--js //存储JS文件 | |--css //存储css文件 | index.tpl //用户首页 | login.tpl //登陆首页 |--zh-CN //中文模板 | |--images | |--js | |--css | index.tpl | login.tpl渲染时把lang拼进模板路径并把Lang传给模板s1, _ : template.ParseFiles(views/ lang /index.tpl) VV.Lang lang s1.Execute(os.Stdout, VV)index.tpl内部的静态资源引用同样基于{{.Lang}}!-- js文件 -- script typetext/javascript srcviews/{{.Lang}}/js/jquery/jquery-1.8.0.min.js/script !-- css文件 -- link hrefviews/{{.Lang}}/css/bootstrap-responsive.min.css relstylesheet !-- 图片文件 -- img srcviews/{{.Lang}}/images/btn.png小结本地化资源本质上都以 key-value 方式存储文本直接输出时间日期、货币先经fmt.Printf等格式化函数处理视图与静态资源最简单——在路径中增加lang即可。这种结构非常便于扩展。四、核心回顾三如何构建国际化站点多语言包管理与模板函数管理多个本地包单语言只需一个配置文件多语言则要设计目录组织方便后续语言扩展。本章的组织方式是把 locale 相关文件放在config/locales下例如同时支持中英文时放置en.json和zh.json# zh.json { zh: { submit: 提交, create: 创建 } } # en.json { en: { submit: Submit, create: Create } }加载与使用 go-i18n 包的方式如下Tr : i18n.NewLocale() Tr.LoadPath(config/locales)fmt.Println(Tr.Translate(submit)) // 输出 Submit Tr.SetLocale(zh) fmt.Println(Tr.Translate(submit)) // 输出 提交自动加载默认本地包go-i18n 预加载了时间格式、货币格式等默认格式信息用户可在自定义配置时改写这些默认值。其加载逻辑loadDefaultTranslations会遍历语言目录下的所有文件目录则递归加载文件则按文件名匹配 locale 后逐个解析func (il *IL) loadDefaultTranslations(dirPath string) error { dir, err : os.Open(dirPath) if err ! nil { return err } defer dir.Close() names, err : dir.Readdirnames(-1) if err ! nil { return err } for _, name : range names { fullPath : path.Join(dirPath, name) fi, err : os.Stat(fullPath) if err ! nil { return err } if fi.IsDir() { if err : il.loadTranslations(fullPath); err ! nil { return err } } else if locale : il.matchingLocaleFromFileName(name); locale ! { file, err : os.Open(fullPath) if err ! nil { return err } defer file.Close() if err : il.loadTranslation(file, locale); err ! nil { return err } } } return nil }默认配置文件按zh.json、en.json、en-US.json等命名可不断扩展支持更多语言。当没有自定义时间信息时可直接调用封装好的方法拿到本地化结果// localezh 的情况下执行如下代码 fmt.Println(Tr.Time(time.Now())) // 输出2009年1月08日 星期四 20:37:58 CST fmt.Println(Tr.Time(time.Now(), long)) // 输出2009年1月08日 fmt.Println(Tr.Money(11.11)) // 输出:11.11模板 mapfunc在模板层直接翻译Tr.Translate、Tr.Time、Tr.Money等函数通常运行在逻辑层若想在模板层直接调用可借助 Go 模板的自定义函数机制FuncMap注册包装函数文本信息调用Tr.Translate包装为I18nT并注册为Tfunc I18nT(args ...interface{}) string { ok : false var s string if len(args) 1 { s, ok args[0].(string) } if !ok { s fmt.Sprint(args...) } return Tr.Translate(s) } t.Funcs(template.FuncMap{T: I18nT})模板中使用{{.V.Submit | T}}时间日期调用Tr.Time包装为I18nTimeDate并注册为TDfunc I18nTimeDate(args ...interface{}) string { ok : false var s string if len(args) 1 { s, ok args[0].(string) } if !ok { s fmt.Sprint(args...) } return Tr.Time(s) } t.Funcs(template.FuncMap{TD: I18nTimeDate})模板中使用{{.V.Now | TD}}货币信息调用Tr.Money包装为I18nMoney并注册为Mfunc I18nMoney(args ...interface{}) string { ok : false var s string if len(args) 1 { s, ok args[0].(string) } if !ok { s fmt.Sprint(args...) } return Tr.Money(s) } t.Funcs(template.FuncMap{M: I18nMoney})模板中使用{{.V.Money | M}}小结通过自定义语言包可方便地实现多语言默认情况下 go-i18n 会加载时间、货币等公共配置借助模板函数T/TD/M在 Web 开发中可直接通过模板 pipeline 操作多语言包无需在逻辑层反复转换。五、框架层面的落地go-i18n 与 beego 的集成本章的方案不止停留在库层面还在 第 14.5 节《多语言支持》 中集成进了 beego 框架。beego 中设置三个全局变量承载 i18nTranslation i18n.IL Lang string // 设置语言包zh、en LangPath string // 设置语言包所在位置初始化多语言函数func InitLang() { beego.Translation i18n.NewLocale() beego.Translation.LoadPath(beego.LangPath) beego.Translation.SetLocale(beego.Lang) }并注册三个模板函数便于在模板中直接翻译beegoTplFuncMap[Trans] i18n.I18nT beegoTplFuncMap[TransDate] i18n.I18nTimeDate beegoTplFuncMap[TransMoney] i18n.I18nMoney使用方式为先设置语言与语言包路径并初始化——beego.Lang zh beego.LangPath views/lang beego.InitLang()再在LangPath下放置zh.json、en.json等多语言包结构与上节config/locales下的 JSON 一致随后既可在 controller 中调用翻译func (this *MainController) Get() { this.Data[create] beego.Translation.Translate(create) this.TplNames index.tpl }也可在模板中直接调用翻译函数// 直接文本翻译 {{.create | Trans}} // 时间翻译 {{.time | TransDate}} // 货币翻译 {{.money | TransMoney}}可以看到从库到框架的集成保持了完全一致的抽象locale 的加载、切换、翻译三件套NewLocale/LoadPath/SetLocaleTranslate/Time/Money与模板函数包装是整章方案可复用的核心。六、本章小结go-i18n 库的价值与后续方向通过这一章的学习可以建立对 i18n 操作的完整认知确定 locale域名 / URL 参数 / 客户端信息→ 存储 locale 资源文本、时间日期、货币、视图全部 key-value 化并 JSON 化→ 按 locale 提取与渲染逻辑层Translate/Time/Money模板层T/TD/M。三者闭环即可支撑一个多语言版本的 Web 应用让应用轻松实现国际化且新增语言时只需增加语言包、无需修改应用代码。作者依据本章内容实现的开源解决方案go-i18n正是这一方案的成品化通过它可很方便地实现多语言版本的 Web 应用使应用轻松完成国际化。该库采用默认公共配置 自定义语言包覆盖的加载模型配合 beego 等框架的模板函数注册机制形成了一条低成本的国际化接入路径。作者也在本章小结中提出如果发现该开源库中的错误或缺失之处欢迎参与到这个开源项目中来共同推动该库向 Go 标准库的目标演进这属于作者的项目愿景而非已实现的现状。围绕 go-i18n 的更多细节可继续阅读本章前三节设置默认地区、本地化资源、国际化站点以及框架集成示例 14.5 多语言支持下一章将进入 错误处理、故障排除和测试。赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐Go Web 国际化i18n全流程小结从 Locale 检测到 go-i18n 多语言站点实战Go Web 国际化i18n全流程小结从 Locale 检测到 go i18n 多语言站点实战 本文是《build web application wit文档教程Go Web 应用国际化i18n实战指南从 Locale 设定到 go-i18n 多语言站点构建Go Web 应用国际化i18n实战指南从 Locale 设定到 go i18n 多语言站点构建 本指南以《Build Web Application w文档教程Go Web 应用国际化i18n实战指南语言包设计、go-i18n 落地与多语言站点构建Go Web 应用国际化i18n实战指南语言包设计、go i18n 落地与多语言站点构建 本篇以《Build Web Application with G文档教程上一篇BentoPDF 签名 PDF 工具深度解析内置查看器手写/键入/图片签名与一键展平下一篇终极AMD GPU优化指南Ollama完整配置教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表