From fb1255e6f56df18aa8a45e048cb482dcccf19c54 Mon Sep 17 00:00:00 2001 From: mpmcroy Date: Tue, 23 Jun 2020 14:24:18 -0400 Subject: [PATCH] Adds app language selection picker (#696) * Adds application language selector to side drawer * Auto-formatting * Revert unused message strings * Minor fixes * Rename function * Addresses review comments * Addresses review comments * Addresses review comment * Addresses review comments * Auto-formatting * Addresses review comments * Filter out duplicate language from language picker * Removes zh language code and associated messages file * Adds jsdoc type information for outline-language-picker --- src/server_manager/messages/zh.json | 224 ------------------ src/server_manager/web_app/app.ts | 10 + src/server_manager/web_app/main.ts | 107 +++++---- .../web_app/ui_components/app-root.js | 66 ++++-- .../ui_components/outline-language-picker.js | 93 ++++++++ 5 files changed, 210 insertions(+), 290 deletions(-) delete mode 100644 src/server_manager/messages/zh.json create mode 100644 src/server_manager/web_app/ui_components/outline-language-picker.js diff --git a/src/server_manager/messages/zh.json b/src/server_manager/messages/zh.json deleted file mode 100644 index bb75ca55..00000000 --- a/src/server_manager/messages/zh.json +++ /dev/null @@ -1,224 +0,0 @@ -{ - "about-outline": "Outline 是由 Jigsaw 创立的开放源代码项目,旨在为新闻机构和记者提供更安全的互联网访问方式。

Outline 由 Shadowsocks 提供支持,目前仍处于产品的初级阶段。您可以在 GitHub 上参与代码编写,还可以通过 Reddit 和 Medium 关注我们,及时掌握我们扩展平台范围和添加新功能的最新动态。", - "about-version": "{version} 版", - "aws-lightsail-firewall-0": "转到 {openLink}Amazon Lightsail{closeLink} 实例页面。", - "aws-lightsail-firewall-1": "点击您要用来托管 Outline 的实例。", - "aws-lightsail-firewall-2": "转到“网络”(Networking) 标签页。", - "aws-lightsail-firewall-3": "在“防火墙”(Firewall) 部分,点击“添加其他规则”(Add another)。", - "aws-lightsail-firewall-4": "将“所有 TCP 和 UDP”(All TCP+UDP) 的值设为“应用”(Application)。", - "aws-lightsail-firewall-5": "点击“保存”(Save)。", - "cancel": "取消", - "city-ams": "阿姆斯特丹", - "city-blr": "班加罗尔", - "city-fra": "法兰克福", - "city-lon": "伦敦", - "city-nyc": "纽约", - "city-sfo": "旧金山", - "city-sgp": "新加坡", - "city-tor": "多伦多", - "close": "关闭", - "confirmation-server-destroy": "现有用户将失去访问权限。此操作无法撤消。", - "confirmation-server-destroy-title": "要删除服务器吗?", - "confirmation-server-remove": "此操作会将您的服务器从 Outline 管理器中移除,但不会屏蔽用户对代理的访问权限。您仍然需要手动将 Outline 服务器从您的主机中删除。", - "confirmation-server-remove-title": "要移除服务器吗?", - "data-limits": "流量上限", - "data-limits-description": "在您为此服务器上的访问密钥设置数据传输流量上限后,系统会对此上限与过去 30 天的已传输流量进行比对,如果已达上限,系统会强制执行流量限制措施。此功能将至少于 {date} 才能提供。", - "data-limits-dialog-text": "请转到“设置”标签页以为此服务器上的访问密钥设置数据传输流量上限。", - "data-limits-dialog-title": "避免超额使用流量", - "data-limits-usage": "已使用 {used},共 {total}", - "destroy": "销毁", - "digitalocean-disconnect": "断开连接", - "digitalocean-disconnect-account": "断开 DigitalOcean 帐号与此应用的连接", - "digitalocean-unreachable": "此错误可能是由于您网络上的防火墙所致,也能是因为在与 digitalocean.com 连接时出现暂时性的问题。", - "disabled": "已停用的", - "done": "完成", - "enabled": "已启用的", - "error-connectivity": "无法连接到您的 DigitalOcean 帐号。这有时候是因为 DigitalOcean 或您的互联网连接出现了暂时性的问题。如果重试仍然失败,请重新登录 DigitalOcean,这样应该能解决问题。", - "error-connectivity-title": "连接问题", - "error-do-account-info": "未能获取 DigitalOcean 帐号信息", - "error-do-auth": "未通过 DigitalOcean 的身份验证", - "error-do-regions": "未能获得可用区域的列表", - "error-feedback": "未能提交反馈。请重试。", - "error-hostname-invalid": "必须是 IP 地址或正确的主机名。", - "error-key-add": "未能添加密钥", - "error-key-remove": "未能移除密钥", - "error-key-rename": "未能重命名密钥", - "error-keys-get": "无法加载密钥", - "error-keys-port-bad-input": "端口号必须是介于 1 到 65535 之间的整数。", - "error-keys-port-in-use": "该端口已被此服务器使用。", - "error-licenses": "无法加载许可。", - "error-metrics": "将指标设为已启用时出错", - "error-network": "发生网络错误。", - "error-not-saved": "未能保存", - "error-remove-data-limit": "无法停用访问密钥的流量上限", - "error-server-creation": "创建 Outline 服务器时发生错误。", - "error-server-destroy": "未能销毁服务器", - "error-server-removed": "您的 DigitalOcean 帐号中不会再显示“{serverName}”。", - "error-server-rename": "未能重命名服务器", - "error-server-unreachable": "虽然您的 Outline 服务器已正确安装,但我们无法连接到该服务器。这很可能是因为该服务器的防火墙规则阻止接入连接。请检查防火墙规则,确保端口 1024 - 65535 均允许接入 TCP 连接。", - "error-server-unreachable-title": "无法连接到您的 Outline 服务器", - "error-servers-removed": "您的 DigitalOcean 帐号中不会再显示{serverNames}。", - "error-set-data-limit": "无法设置访问密钥的流量上限", - "error-unexpected": "发生意外错误。", - "experiments": "实验功能", - "experiments-description": "请在此新功能发布前进行测试并向我们提供反馈。", - "experiments-disclaimer": "实验功能正在开发中,并且可能发生变化或从应用中移除。如果您当前正在报告有关指标的情况,指标会包含实验功能的使用情况。要了解详情,请参阅{openLink}数据收集政策{closeLink}。", - "experiments-feedback": "有什么建议吗?{openLink}在此提交反馈。{closeLink}", - "feedback-cloud-provider": "选择云服务提供商", - "feedback-cloud-provider-error": "请选择云服务提供商。", - "feedback-connection": "无法连接到我的服务器", - "feedback-connection-others": "其他人无法连接到我的服务器", - "feedback-disclaimer": "请注意,我们的团队只能用英语回复反馈。", - "feedback-email": "电子邮件地址(可选)", - "feedback-error": "请输入反馈。", - "feedback-explanation-install": "尝试在您的服务器上安装 Outline 时发生错误。如果您找不到解决办法,不妨向我们发送反馈,告诉我们您的电子邮件地址(可选),以便我们回复您。", - "feedback-general": "一般反馈", - "feedback-install": "安装 Outline 时出现了问题", - "feedback-label": "您的反馈", - "feedback-management": "管理我的服务器时出现了问题", - "feedback-other": "其他", - "feedback-privacy": "系统会将您的反馈、电子邮件地址(如果提供的话)以及{openLink}隐私权政策{closeLink}中提及的其他信息发送给 Outline 团队。", - "feedback-submit": "提交", - "feedback-suggestion": "建议", - "feedback-title-generic": "发送反馈", - "feedback-title-install": "Outline 服务器安装失败", - "gcp-click-create": "点击“创建”(Create)。", - "gcp-create-new-project": "{openLink}新建 Google Cloud 项目{closeLink}。", - "gcp-create-new-vm": "{openLink}新建虚拟机实例{closeLink}。", - "gcp-create-project": "创建 Google Cloud 项目", - "gcp-create-server": "创建 Google Cloud 项目", - "gcp-create-vm": "创建虚拟机实例", - "gcp-firewall-create-0": "在 Compute Engine 项目中{openLink}添加新的防火墙规则{closeLink}。", - "gcp-firewall-create-1": "在“名称”(Name) 字段输入“outline”。", - "gcp-firewall-create-2": "在“目标标记”(Target tags) 字段中输入“outline”。", - "gcp-firewall-create-3": "在“源 IP 范围”(Source IP ranges) 字段中输入“0.0.0.0/0”。", - "gcp-firewall-create-4": "在“协议和端口”(Protocols and ports) 下,选择“全部允许”(Allow all)。", - "gcp-name-your-project": "在“项目名称”(Project name) 字段为您的项目命名。", - "gcp-select-machine-type": "在“机器类型”(Machine type) 下方选择“f1-micro”", - "gcp-select-networking": "点击“管理、安全、磁盘、网络、单独租用”(Management, security, disks, networking, sole tenancy),然后点击“网络”(Networking)", - "gcp-select-region": "在“区域”(Region) 下方选择一个靠近服务器用户所在位置的区域。", - "gcp-type-network-tag": "在“网络标记”(Network tags) 字段中输入“outline”", - "gcp-type-outline-server": "在“名称”(Name) 字段中输入“outline-server”。", - "key": "密钥 {keyId}", - "manual-server-assign-firewall": "指定防火墙规则", - "manual-server-assign-group": "分配安全组", - "manual-server-create-firewall": "创建防火墙规则", - "manual-server-create-group": "创建安全组", - "manual-server-description": "您可以按照以下步骤在 {cloudProvider} Linux 服务器上安装 Outline。", - "manual-server-firewall": "配置防火墙", - "manual-server-install-paste": "将安装脚本的输出结果粘贴到此处。", - "manual-server-install-run": "登录您的服务器,然后运行下面的命令。", - "manual-server-instructions": "操作说明", - "manual-server-show-me": "前往配置界面", - "manual-server-title": "请按照以下说明操作", - "metrics-description": "分享经过匿名化处理的指标,以帮助提升 Outline 的可靠性和性能,您和服务器的共享对象都能从中受益。{openLink}了解详情。{closeLink}", - "metrics-share": "共享指标", - "metrics-skip": "跳过", - "metrics-title": "指标共享", - "nav-about": "关于", - "nav-data-collection": "数据收集", - "nav-feedback": "反馈", - "nav-help": "帮助", - "nav-licenses": "许可", - "nav-privacy": "隐私权", - "nav-terms": "条款", - "notification-app-update": "已下载 Outline 管理器的更新版本。系统会在您重启该应用时安装此版本。", - "notification-feedback-thanks": "感谢您帮助我们改进产品!欢迎您随时提供反馈。", - "notification-key-added": "密钥已添加", - "notification-key-removed": "密钥已移除", - "notification-server-destroyed": "服务器已销毁", - "notification-server-exists": "服务器已添加", - "notification-server-removed": "服务器已移除", - "oauth-account-active": "您的 DigitalOcean 帐号已激活。", - "oauth-account-active-tag": "帐号已激活!正在加载服务器地址…", - "oauth-activate-account": "激活您的 DigitalOcean 帐号。", - "oauth-billing": "在 digitalocean.com 上输入您的结算信息,输入完毕后再回到此应用。", - "oauth-billing-tag": "请输入结算信息…", - "oauth-connect-description": "授予对您帐号的访问权限后,Outline 便可以帮助您轻松创建服务器并建立连接。", - "oauth-connect-tag": "正在等待连接您的帐号…", - "oauth-connect-title": "登录或创建 DigitalOcean 帐号。", - "oauth-sign-out": "退出", - "oauth-verify": "查看您的收件箱,找到来自 DigitalOcean 的电子邮件,点击其中的链接以确认您的帐号。", - "oauth-verify-tag": "确认您的电子邮件地址…", - "okay": "确定", - "region-description": "您将从该位置接入互联网。", - "region-setup": "安装 Outline", - "region-title": "选择服务器位置。", - "remove": "移除", - "retry": "重试", - "save": "保存", - "saved": "已保存", - "saving": "正在保存…", - "server-access": "服务器访问密钥", - "server-access-key-new": "添加新密钥", - "server-access-key-rename": "重命名", - "server-access-keys": "访问密钥", - "server-connect-devices": "连接您的设备", - "server-connections": "连接", - "server-data-transfer": "已传输流量 / 过去 30 天", - "server-data-used": "流量限额的已用比例 / 过去 30 天", - "server-destroy": "销毁服务器", - "server-help-access-key-description": "可与朋友分享您的访问密钥,让他们也能连接到您的 Outline 服务器。您的朋友在自己的所有设备上都可以使用这个访问密钥。", - "server-help-access-key-next": "下一步", - "server-help-access-key-title": "创建密钥,以共享访问权限", - "server-help-connection-description": "点击此处即可安装 Outline 客户端应用,安装过程中需要用到您的个人访问密钥来连接 Outline 服务器。", - "server-help-connection-ok": "知道了!", - "server-help-connection-title": "您尚未连接到服务器!", - "server-keys": "密钥", - "server-my-access-key": "我的访问密钥", - "server-name": "{serverLocation} Outline 服务器", - "server-remove": "移除服务器", - "server-settings": "设置", - "server-unreachable": "无法连接到服务器", - "server-unreachable-description": "无法连接到此服务器。", - "server-unreachable-managed-description": "请重试,或者将此服务器从应用中移除。", - "server-unreachable-manual-description": "请重试,或者销毁此服务器及虚拟主机。", - "server-usage": "流量使用情况(过去 30 天)", - "servers-add": "添加服务器", - "servers-digitalocean": "DigitalOcean 服务器", - "servers-manual": "服务器", - "settings-access-key-port": "用于新访问密钥的端口", - "settings-metrics-header": "共享匿名指标", - "settings-server-api-url": "管理 API 的网址", - "settings-server-cost": "每月费用", - "settings-server-creation": "创建日期", - "settings-server-hostname": "主机名", - "settings-server-id": "服务器 ID", - "settings-server-info": "服务器信息", - "settings-server-location": "服务器位置", - "settings-server-name": "名称", - "settings-server-rename": "为服务器设定新名称。请注意,如果您之前邀请了其他用户连接到此服务器,则这些用户的设备上不会相应地更新此服务器的名称。", - "settings-server-version": "服务器版本", - "settings-transfer-limit": "数据传输限额", - "setup-action": "安装", - "setup-advanced": "高级", - "setup-anywhere": "随时随地安装 Outline", - "setup-create": "创建服务器", - "setup-description": "没有服务器?您可以创建一个 DigitalOcean 帐号。", - "setup-do-cancel": "可随时取消", - "setup-do-cost": "每月只需 5 美元", - "setup-do-create": "使用您的 DigitalOcean 帐号创建一个新服务器,就能以每 30 天 5 美元的价格额外获享 1 TB 的数据传输流量。", - "setup-do-data": "1 TB 数据传输限额", - "setup-do-description": "此过程最长可能需要 2 分钟。您随时可以销毁此服务器。", - "setup-do-easiest": "最轻松的设置流程", - "setup-do-title": "正在安装 Outline。", - "setup-firewall-instructions": "防火墙配置说明", - "setup-recommended": "推荐", - "setup-simple-commands": "简单易用的安装命令", - "setup-step-by-step": "提供分步设置指南", - "setup-tested": "在 VULTR、Linode 和 Liquid Web 上测试过", - "setup-title": "选择用于安装 Outline 的云服务。", - "share-description": "复制此邀请,并使用您信任的通讯工具发送。{openLink}需要帮助?{closeLink}", - "share-invite": "诚邀您连接到我的 Outline 服务器。无论您身在何处,都可以通过该服务器访问开放的互联网。请点击下面的邀请链接,按照其中的说明操作来下载 Outline 应用并建立连接。", - "share-invite-copied": "已复制到剪贴板", - "share-invite-copy": "复制邀请", - "share-invite-copy-access-key": "复制您的访问密钥:", - "share-invite-instructions": "在 GitHub 上按照我们所发邀请中的说明操作:", - "share-invite-trouble": "无法访问邀请链接?", - "share-title": "共享访问权限", - "survey-data-limits-title": "帮助我们了解如何改进流量限制功能", - "survey-decline": "拒绝", - "survey-disclaimer": "点击“继续”后,您将转到 Google 表单并需要填写一份简短的调查问卷。我们建议您在连接到 Outline 的同时填写调查问卷。", - "survey-go-to-survey": "转到调查问卷", - "terms-of-service": "我已阅读并理解了 {openLink}Outline 服务条款{closeLink}" -} \ No newline at end of file diff --git a/src/server_manager/web_app/app.ts b/src/server_manager/web_app/app.ts index f77c4857..f4ff1abd 100644 --- a/src/server_manager/web_app/app.ts +++ b/src/server_manager/web_app/app.ts @@ -247,6 +247,10 @@ export class App { } }); + appRoot.addEventListener('SetLanguageRequested', (event: CustomEvent) => { + this.setAppLanguage(event.detail.languageCode, event.detail.languageDir); + }); + appRoot.addEventListener('ServerRenameRequested', (event: CustomEvent) => { this.renameServer(event.detail.newName); }); @@ -1233,4 +1237,10 @@ export class App { this.showCreateServer(); }); } + + private setAppLanguage(languageCode: string, languageDir: string) { + this.appRoot.setLanguage(languageCode, languageDir); + document.documentElement.setAttribute('dir', languageDir); + window.localStorage.setItem('overrideLanguage', languageCode); + } } diff --git a/src/server_manager/web_app/main.ts b/src/server_manager/web_app/main.ts index 3a99a743..d90d31eb 100644 --- a/src/server_manager/web_app/main.ts +++ b/src/server_manager/web_app/main.ts @@ -26,59 +26,69 @@ import {ManualServerRepository} from './manual_server'; import {DEFAULT_PROMPT_IMPRESSION_DELAY_MS, OutlineSurveys} from './survey'; import {AppRoot} from './ui_components/app-root.js'; -const SUPPORTED_LANGUAGES: {[key: string]: {id: string, dir: string}} = { - 'am': {id: 'am', dir: 'ltr'}, - 'ar': {id: 'ar', dir: 'rtl'}, - 'bg': {id: 'bg', dir: 'ltr'}, - 'ca': {id: 'ca', dir: 'ltr'}, - 'cs': {id: 'cs', dir: 'ltr'}, - 'da': {id: 'da', dir: 'ltr'}, - 'de': {id: 'de', dir: 'ltr'}, - 'el': {id: 'el', dir: 'ltr'}, - 'en': {id: 'en', dir: 'ltr'}, - 'es-419': {id: 'es-419', dir: 'ltr'}, - 'fa': {id: 'fa', dir: 'rtl'}, - 'fi': {id: 'fi', dir: 'ltr'}, - 'fil': {id: 'fil', dir: 'ltr'}, - 'fr': {id: 'fr', dir: 'ltr'}, - 'he': {id: 'he', dir: 'rtl'}, - 'hi': {id: 'hi', dir: 'ltr'}, - 'hr': {id: 'hr', dir: 'ltr'}, - 'hu': {id: 'hu', dir: 'ltr'}, - 'id': {id: 'id', dir: 'ltr'}, - 'it': {id: 'it', dir: 'ltr'}, - 'ja': {id: 'ja', dir: 'ltr'}, - 'ko': {id: 'ko', dir: 'ltr'}, - 'km': {id: 'km', dir: 'ltr'}, - 'lt': {id: 'lt', dir: 'ltr'}, - 'lv': {id: 'lv', dir: 'ltr'}, - 'nl': {id: 'nl', dir: 'ltr'}, - 'no': {id: 'no', dir: 'ltr'}, - 'pl': {id: 'pl', dir: 'ltr'}, - 'pt-BR': {id: 'pt-BR', dir: 'ltr'}, - 'ro': {id: 'ro', dir: 'ltr'}, - 'ru': {id: 'ru', dir: 'ltr'}, - 'sk': {id: 'sk', dir: 'ltr'}, - 'sl': {id: 'sl', dir: 'ltr'}, - 'sr': {id: 'sr', dir: 'ltr'}, - 'sr-Latn': {id: 'sr-Latn', dir: 'ltr'}, - 'sv': {id: 'sv', dir: 'ltr'}, - 'th': {id: 'th', dir: 'ltr'}, - 'tr': {id: 'tr', dir: 'ltr'}, - 'uk': {id: 'uk', dir: 'ltr'}, - 'ur': {id: 'ur', dir: 'rtl'}, - 'vi': {id: 'vi', dir: 'ltr'}, - 'zh': {id: 'zh', dir: 'ltr'}, - 'zh-CN': {id: 'zh-CN', dir: 'ltr'}, - 'zh-TW': {id: 'zh-TW', dir: 'ltr'}, +type LanguageDef = {id: string, name: string, dir: string}; +const SUPPORTED_LANGUAGES: {[key: string]: LanguageDef} = { + 'am': {id: 'am', name: 'አማርኛ', dir: 'ltr'}, + 'ar': {id: 'ar', name: 'العربية', dir: 'rtl'}, + 'bg': {id: 'bg', name: 'Български', dir: 'ltr'}, + 'ca': {id: 'ca', name: 'Català', dir: 'ltr'}, + 'cs': {id: 'cs', name: 'Česky', dir: 'ltr'}, + 'da': {id: 'da', name: 'Dansk', dir: 'ltr'}, + 'de': {id: 'de', name: 'Deutsch', dir: 'ltr'}, + 'el': {id: 'el', name: 'Ελληνικά', dir: 'ltr'}, + 'en': {id: 'en', name: 'English', dir: 'ltr'}, + 'es-419': {id: 'es-419', name: 'Español', dir: 'ltr'}, + 'fa': {id: 'fa', name: 'فارسی', dir: 'rtl'}, + 'fi': {id: 'fi', name: 'Suomi', dir: 'ltr'}, + 'fil': {id: 'fil', name: 'Wikang Filipino', dir: 'ltr'}, + 'fr': {id: 'fr', name: 'Français', dir: 'ltr'}, + 'he': {id: 'he', name: 'עברית', dir: 'rtl'}, + 'hi': {id: 'hi', name: 'हिन्दी', dir: 'ltr'}, + 'hr': {id: 'hr', name: 'Hrvatski', dir: 'ltr'}, + 'hu': {id: 'hu', name: 'Magyar', dir: 'ltr'}, + 'id': {id: 'id', name: 'Bahasa Indonesia', dir: 'ltr'}, + 'it': {id: 'it', name: 'Italiano', dir: 'ltr'}, + 'ja': {id: 'ja', name: '日本語', dir: 'ltr'}, + 'km': {id: 'km', name: 'ភាសាខ្មែរ', dir: 'ltr'}, + 'ko': {id: 'ko', name: '한국어', dir: 'ltr'}, + 'lt': {id: 'lt', name: 'Lietuvių', dir: 'ltr'}, + 'lv': {id: 'lv', name: 'Latviešu', dir: 'ltr'}, + 'nl': {id: 'nl', name: 'Nederlands', dir: 'ltr'}, + 'no': {id: 'no', name: 'Norsk (bokmål / riksmål)', dir: 'ltr'}, + 'pl': {id: 'pl', name: 'Polski', dir: 'ltr'}, + 'pt-BR': {id: 'pt-BR', name: 'Português', dir: 'ltr'}, + 'ro': {id: 'ro', name: 'Română', dir: 'ltr'}, + 'ru': {id: 'ru', name: 'Русский', dir: 'ltr'}, + 'sk': {id: 'sk', name: 'Slovenčina', dir: 'ltr'}, + 'sl': {id: 'sl', name: 'Slovenščina', dir: 'ltr'}, + 'sr': {id: 'sr', name: 'Српски', dir: 'ltr'}, + 'sr-Latn': {id: 'sr-Latn', name: 'Srpski', dir: 'ltr'}, + 'sv': {id: 'sv', name: 'Svenska', dir: 'ltr'}, + 'th': {id: 'th', name: 'ไทย', dir: 'ltr'}, + 'tr': {id: 'tr', name: 'Türkçe', dir: 'ltr'}, + 'uk': {id: 'uk', name: 'Українська', dir: 'ltr'}, + 'ur': {id: 'ur', name: 'اردو', dir: 'rtl'}, + 'vi': {id: 'vi', name: 'Việtnam', dir: 'ltr'}, + 'zh-CN': {id: 'zh-CN', name: '简体中文', dir: 'ltr'}, + 'zh-TW': {id: 'zh-TW', name: '繁體中文‬‬‪‬', dir: 'ltr'}, }; function getLanguageToUse(): i18n.LanguageCode { const supportedLanguages = i18n.languageList(Object.keys(SUPPORTED_LANGUAGES)); + const preferredLanguages = i18n.getBrowserLanguages(); + const overrideLanguage = window.localStorage.getItem('overrideLanguage'); + if (overrideLanguage) { + preferredLanguages.unshift(new i18n.LanguageCode(overrideLanguage)); + } const defaultLanguage = new i18n.LanguageCode('en'); - const userLanguages = i18n.getBrowserLanguages(); return new i18n.LanguageMatcher(supportedLanguages, defaultLanguage) - .getBestSupportedLanguage(userLanguages); + .getBestSupportedLanguage(preferredLanguages); +} + +function sortLanguageDefsByName(languageDefs: LanguageDef[]) { + return languageDefs.sort((a, b) => { + return a.name > b.name ? 1 : -1; + }); } document.addEventListener('WebComponentsReady', () => { @@ -103,6 +113,9 @@ document.addEventListener('WebComponentsReady', () => { // NOTE: this cast is safe and allows us to leverage Polymer typings since we haven't migrated to // Polymer 3, which adds typescript support. const appRoot = document.getElementById('appRoot') as unknown as AppRoot; + + const filteredLanguageDefs = Object.values(SUPPORTED_LANGUAGES); + appRoot.supportedLanguages = sortLanguageDefsByName(filteredLanguageDefs); appRoot.setLanguage(language.string(), languageDirection); new App( appRoot, version, digitalocean_api.createDigitalOceanSession, diff --git a/src/server_manager/web_app/ui_components/app-root.js b/src/server_manager/web_app/ui_components/app-root.js index b8a79dc1..5c601872 100644 --- a/src/server_manager/web_app/ui_components/app-root.js +++ b/src/server_manager/web_app/ui_components/app-root.js @@ -32,6 +32,7 @@ import './outline-do-oauth-step.js'; import './outline-feedback-dialog.js'; import './outline-survey-dialog.js'; import './outline-intro-step.js'; +import './outline-language-picker.js'; import './outline-manual-server-entry.js'; import './outline-modal-dialog.js'; import './outline-region-picker-step.js'; @@ -229,12 +230,28 @@ export class AppRoot extends mixinBehaviors #appDrawer a:focus { outline: none; } + #links-footer { + margin-top: 36px; + } .legal-links { - margin: 36px -6px 0; + margin: 0 -6px; } .legal-links > * { margin: 0 6px; } + #language-row { + display: flex; + align-items: center; + } + #language-icon { + padding-top: 10px; + } + #language-dropdown { + padding-left: 22px; + --paper-input-container: { + width: 156px; + }; + } app-toolbar [main-title] img { height: 16px; margin-top: 8px; @@ -367,15 +384,21 @@ export class AppRoot extends mixinBehaviors - - [[localize('nav-data-collection')]] + + [[localize('nav-data-collection')]] [[localize('nav-feedback')]] - [[localize('nav-help')]] + [[localize('nav-help')]] [[localize('nav-about')]] -