diff --git a/internal/core/wordlists.go b/internal/core/wordlists.go
index 0587d6c..4a8a89d 100644
--- a/internal/core/wordlists.go
+++ b/internal/core/wordlists.go
@@ -19,17 +19,18 @@ var wordlistFS embed.FS
type Lang string
const (
- LangEN Lang = "en"
- LangES Lang = "es"
- LangFR Lang = "fr"
- LangDE Lang = "de"
- LangSL Lang = "sl"
- LangPT Lang = "pt"
+ LangEN Lang = "en"
+ LangES Lang = "es"
+ LangFR Lang = "fr"
+ LangDE Lang = "de"
+ LangSL Lang = "sl"
+ LangPT Lang = "pt"
+ LangZH_TW Lang = "zh-TW"
)
// AllLangs returns all supported word list languages.
func AllLangs() []Lang {
- return []Lang{LangEN, LangES, LangFR, LangDE, LangSL, LangPT}
+ return []Lang{LangEN, LangES, LangFR, LangDE, LangSL, LangPT, LangZH_TW}
}
// WordListInfo describes a BIP39 word list: its source, expected hash, and words.
@@ -60,6 +61,7 @@ var wordListSpecs = []wordListSpec{
{LangDE, "wordlists/german.txt", "https://github.com/dys2p/wordlists-de/blob/43553378b71ac06467e4654372ac249f15e16f4d/de-2048-v1.txt", "7965dc8c6b413ccb635d3021043365e18df0367bf5413a50a069a98addfe4e1d"},
{LangSL, "wordlists/slovenian.txt", "https://github.com/StellarStoic/BIP39_Exotica/8a5c0d93be825fab837dd293c94c635d6a39aa70/main/WRDL/nonStandard/slovenian.txt", "bdc73f14501843be9ae38fea61d6070298df4a83c67a8710e9755c557880467a"},
{LangPT, "wordlists/portuguese.txt", "https://github.com/bitcoin/bips/blob/ed7af6ae7e80c90bcfc69b3936073505e2fc2503/bip-0039/portuguese.txt", "2685e9c194c82ae67e10ba59d9ea5345a23dc093e92276fc5361f6667d79cd3f"},
+ {LangZH_TW, "wordlists/chinese_traditional.txt", "https://github.com/bitcoin/bips/blob/7d77befd2b14359b9386fc1f9fb15f82d418fb34/bip-0039/chinese_traditional.txt", "417b26b3d8500a4ae3d59717d7011952db6fc2fb84b807f3f94ac734e89c1b5f"},
}
var (
diff --git a/internal/core/wordlists/chinese_traditional.txt b/internal/core/wordlists/chinese_traditional.txt
new file mode 100644
index 0000000..9b02047
--- /dev/null
+++ b/internal/core/wordlists/chinese_traditional.txt
@@ -0,0 +1,2048 @@
+的
+一
+是
+在
+不
+了
+有
+和
+人
+這
+中
+大
+為
+上
+個
+國
+我
+以
+要
+他
+時
+來
+用
+們
+生
+到
+作
+地
+於
+出
+就
+分
+對
+成
+會
+可
+主
+發
+年
+動
+同
+工
+也
+能
+下
+過
+子
+說
+產
+種
+面
+而
+方
+後
+多
+定
+行
+學
+法
+所
+民
+得
+經
+十
+三
+之
+進
+著
+等
+部
+度
+家
+電
+力
+裡
+如
+水
+化
+高
+自
+二
+理
+起
+小
+物
+現
+實
+加
+量
+都
+兩
+體
+制
+機
+當
+使
+點
+從
+業
+本
+去
+把
+性
+好
+應
+開
+它
+合
+還
+因
+由
+其
+些
+然
+前
+外
+天
+政
+四
+日
+那
+社
+義
+事
+平
+形
+相
+全
+表
+間
+樣
+與
+關
+各
+重
+新
+線
+內
+數
+正
+心
+反
+你
+明
+看
+原
+又
+麼
+利
+比
+或
+但
+質
+氣
+第
+向
+道
+命
+此
+變
+條
+只
+沒
+結
+解
+問
+意
+建
+月
+公
+無
+系
+軍
+很
+情
+者
+最
+立
+代
+想
+已
+通
+並
+提
+直
+題
+黨
+程
+展
+五
+果
+料
+象
+員
+革
+位
+入
+常
+文
+總
+次
+品
+式
+活
+設
+及
+管
+特
+件
+長
+求
+老
+頭
+基
+資
+邊
+流
+路
+級
+少
+圖
+山
+統
+接
+知
+較
+將
+組
+見
+計
+別
+她
+手
+角
+期
+根
+論
+運
+農
+指
+幾
+九
+區
+強
+放
+決
+西
+被
+幹
+做
+必
+戰
+先
+回
+則
+任
+取
+據
+處
+隊
+南
+給
+色
+光
+門
+即
+保
+治
+北
+造
+百
+規
+熱
+領
+七
+海
+口
+東
+導
+器
+壓
+志
+世
+金
+增
+爭
+濟
+階
+油
+思
+術
+極
+交
+受
+聯
+什
+認
+六
+共
+權
+收
+證
+改
+清
+美
+再
+採
+轉
+更
+單
+風
+切
+打
+白
+教
+速
+花
+帶
+安
+場
+身
+車
+例
+真
+務
+具
+萬
+每
+目
+至
+達
+走
+積
+示
+議
+聲
+報
+鬥
+完
+類
+八
+離
+華
+名
+確
+才
+科
+張
+信
+馬
+節
+話
+米
+整
+空
+元
+況
+今
+集
+溫
+傳
+土
+許
+步
+群
+廣
+石
+記
+需
+段
+研
+界
+拉
+林
+律
+叫
+且
+究
+觀
+越
+織
+裝
+影
+算
+低
+持
+音
+眾
+書
+布
+复
+容
+兒
+須
+際
+商
+非
+驗
+連
+斷
+深
+難
+近
+礦
+千
+週
+委
+素
+技
+備
+半
+辦
+青
+省
+列
+習
+響
+約
+支
+般
+史
+感
+勞
+便
+團
+往
+酸
+歷
+市
+克
+何
+除
+消
+構
+府
+稱
+太
+準
+精
+值
+號
+率
+族
+維
+劃
+選
+標
+寫
+存
+候
+毛
+親
+快
+效
+斯
+院
+查
+江
+型
+眼
+王
+按
+格
+養
+易
+置
+派
+層
+片
+始
+卻
+專
+狀
+育
+廠
+京
+識
+適
+屬
+圓
+包
+火
+住
+調
+滿
+縣
+局
+照
+參
+紅
+細
+引
+聽
+該
+鐵
+價
+嚴
+首
+底
+液
+官
+德
+隨
+病
+蘇
+失
+爾
+死
+講
+配
+女
+黃
+推
+顯
+談
+罪
+神
+藝
+呢
+席
+含
+企
+望
+密
+批
+營
+項
+防
+舉
+球
+英
+氧
+勢
+告
+李
+台
+落
+木
+幫
+輪
+破
+亞
+師
+圍
+注
+遠
+字
+材
+排
+供
+河
+態
+封
+另
+施
+減
+樹
+溶
+怎
+止
+案
+言
+士
+均
+武
+固
+葉
+魚
+波
+視
+僅
+費
+緊
+愛
+左
+章
+早
+朝
+害
+續
+輕
+服
+試
+食
+充
+兵
+源
+判
+護
+司
+足
+某
+練
+差
+致
+板
+田
+降
+黑
+犯
+負
+擊
+范
+繼
+興
+似
+餘
+堅
+曲
+輸
+修
+故
+城
+夫
+夠
+送
+筆
+船
+佔
+右
+財
+吃
+富
+春
+職
+覺
+漢
+畫
+功
+巴
+跟
+雖
+雜
+飛
+檢
+吸
+助
+昇
+陽
+互
+初
+創
+抗
+考
+投
+壞
+策
+古
+徑
+換
+未
+跑
+留
+鋼
+曾
+端
+責
+站
+簡
+述
+錢
+副
+盡
+帝
+射
+草
+衝
+承
+獨
+令
+限
+阿
+宣
+環
+雙
+請
+超
+微
+讓
+控
+州
+良
+軸
+找
+否
+紀
+益
+依
+優
+頂
+礎
+載
+倒
+房
+突
+坐
+粉
+敵
+略
+客
+袁
+冷
+勝
+絕
+析
+塊
+劑
+測
+絲
+協
+訴
+念
+陳
+仍
+羅
+鹽
+友
+洋
+錯
+苦
+夜
+刑
+移
+頻
+逐
+靠
+混
+母
+短
+皮
+終
+聚
+汽
+村
+雲
+哪
+既
+距
+衛
+停
+烈
+央
+察
+燒
+迅
+境
+若
+印
+洲
+刻
+括
+激
+孔
+搞
+甚
+室
+待
+核
+校
+散
+侵
+吧
+甲
+遊
+久
+菜
+味
+舊
+模
+湖
+貨
+損
+預
+阻
+毫
+普
+穩
+乙
+媽
+植
+息
+擴
+銀
+語
+揮
+酒
+守
+拿
+序
+紙
+醫
+缺
+雨
+嗎
+針
+劉
+啊
+急
+唱
+誤
+訓
+願
+審
+附
+獲
+茶
+鮮
+糧
+斤
+孩
+脫
+硫
+肥
+善
+龍
+演
+父
+漸
+血
+歡
+械
+掌
+歌
+沙
+剛
+攻
+謂
+盾
+討
+晚
+粒
+亂
+燃
+矛
+乎
+殺
+藥
+寧
+魯
+貴
+鐘
+煤
+讀
+班
+伯
+香
+介
+迫
+句
+豐
+培
+握
+蘭
+擔
+弦
+蛋
+沉
+假
+穿
+執
+答
+樂
+誰
+順
+煙
+縮
+徵
+臉
+喜
+松
+腳
+困
+異
+免
+背
+星
+福
+買
+染
+井
+概
+慢
+怕
+磁
+倍
+祖
+皇
+促
+靜
+補
+評
+翻
+肉
+踐
+尼
+衣
+寬
+揚
+棉
+希
+傷
+操
+垂
+秋
+宜
+氫
+套
+督
+振
+架
+亮
+末
+憲
+慶
+編
+牛
+觸
+映
+雷
+銷
+詩
+座
+居
+抓
+裂
+胞
+呼
+娘
+景
+威
+綠
+晶
+厚
+盟
+衡
+雞
+孫
+延
+危
+膠
+屋
+鄉
+臨
+陸
+顧
+掉
+呀
+燈
+歲
+措
+束
+耐
+劇
+玉
+趙
+跳
+哥
+季
+課
+凱
+胡
+額
+款
+紹
+卷
+齊
+偉
+蒸
+殖
+永
+宗
+苗
+川
+爐
+岩
+弱
+零
+楊
+奏
+沿
+露
+桿
+探
+滑
+鎮
+飯
+濃
+航
+懷
+趕
+庫
+奪
+伊
+靈
+稅
+途
+滅
+賽
+歸
+召
+鼓
+播
+盤
+裁
+險
+康
+唯
+錄
+菌
+純
+借
+糖
+蓋
+橫
+符
+私
+努
+堂
+域
+槍
+潤
+幅
+哈
+竟
+熟
+蟲
+澤
+腦
+壤
+碳
+歐
+遍
+側
+寨
+敢
+徹
+慮
+斜
+薄
+庭
+納
+彈
+飼
+伸
+折
+麥
+濕
+暗
+荷
+瓦
+塞
+床
+築
+惡
+戶
+訪
+塔
+奇
+透
+梁
+刀
+旋
+跡
+卡
+氯
+遇
+份
+毒
+泥
+退
+洗
+擺
+灰
+彩
+賣
+耗
+夏
+擇
+忙
+銅
+獻
+硬
+予
+繁
+圈
+雪
+函
+亦
+抽
+篇
+陣
+陰
+丁
+尺
+追
+堆
+雄
+迎
+泛
+爸
+樓
+避
+謀
+噸
+野
+豬
+旗
+累
+偏
+典
+館
+索
+秦
+脂
+潮
+爺
+豆
+忽
+托
+驚
+塑
+遺
+愈
+朱
+替
+纖
+粗
+傾
+尚
+痛
+楚
+謝
+奮
+購
+磨
+君
+池
+旁
+碎
+骨
+監
+捕
+弟
+暴
+割
+貫
+殊
+釋
+詞
+亡
+壁
+頓
+寶
+午
+塵
+聞
+揭
+炮
+殘
+冬
+橋
+婦
+警
+綜
+招
+吳
+付
+浮
+遭
+徐
+您
+搖
+谷
+贊
+箱
+隔
+訂
+男
+吹
+園
+紛
+唐
+敗
+宋
+玻
+巨
+耕
+坦
+榮
+閉
+灣
+鍵
+凡
+駐
+鍋
+救
+恩
+剝
+凝
+鹼
+齒
+截
+煉
+麻
+紡
+禁
+廢
+盛
+版
+緩
+淨
+睛
+昌
+婚
+涉
+筒
+嘴
+插
+岸
+朗
+莊
+街
+藏
+姑
+貿
+腐
+奴
+啦
+慣
+乘
+夥
+恢
+勻
+紗
+扎
+辯
+耳
+彪
+臣
+億
+璃
+抵
+脈
+秀
+薩
+俄
+網
+舞
+店
+噴
+縱
+寸
+汗
+掛
+洪
+賀
+閃
+柬
+爆
+烯
+津
+稻
+牆
+軟
+勇
+像
+滾
+厘
+蒙
+芳
+肯
+坡
+柱
+盪
+腿
+儀
+旅
+尾
+軋
+冰
+貢
+登
+黎
+削
+鑽
+勒
+逃
+障
+氨
+郭
+峰
+幣
+港
+伏
+軌
+畝
+畢
+擦
+莫
+刺
+浪
+秘
+援
+株
+健
+售
+股
+島
+甘
+泡
+睡
+童
+鑄
+湯
+閥
+休
+匯
+舍
+牧
+繞
+炸
+哲
+磷
+績
+朋
+淡
+尖
+啟
+陷
+柴
+呈
+徒
+顏
+淚
+稍
+忘
+泵
+藍
+拖
+洞
+授
+鏡
+辛
+壯
+鋒
+貧
+虛
+彎
+摩
+泰
+幼
+廷
+尊
+窗
+綱
+弄
+隸
+疑
+氏
+宮
+姐
+震
+瑞
+怪
+尤
+琴
+循
+描
+膜
+違
+夾
+腰
+緣
+珠
+窮
+森
+枝
+竹
+溝
+催
+繩
+憶
+邦
+剩
+幸
+漿
+欄
+擁
+牙
+貯
+禮
+濾
+鈉
+紋
+罷
+拍
+咱
+喊
+袖
+埃
+勤
+罰
+焦
+潛
+伍
+墨
+欲
+縫
+姓
+刊
+飽
+仿
+獎
+鋁
+鬼
+麗
+跨
+默
+挖
+鏈
+掃
+喝
+袋
+炭
+污
+幕
+諸
+弧
+勵
+梅
+奶
+潔
+災
+舟
+鑑
+苯
+訟
+抱
+毀
+懂
+寒
+智
+埔
+寄
+屆
+躍
+渡
+挑
+丹
+艱
+貝
+碰
+拔
+爹
+戴
+碼
+夢
+芽
+熔
+赤
+漁
+哭
+敬
+顆
+奔
+鉛
+仲
+虎
+稀
+妹
+乏
+珍
+申
+桌
+遵
+允
+隆
+螺
+倉
+魏
+銳
+曉
+氮
+兼
+隱
+礙
+赫
+撥
+忠
+肅
+缸
+牽
+搶
+博
+巧
+殼
+兄
+杜
+訊
+誠
+碧
+祥
+柯
+頁
+巡
+矩
+悲
+灌
+齡
+倫
+票
+尋
+桂
+鋪
+聖
+恐
+恰
+鄭
+趣
+抬
+荒
+騰
+貼
+柔
+滴
+猛
+闊
+輛
+妻
+填
+撤
+儲
+簽
+鬧
+擾
+紫
+砂
+遞
+戲
+吊
+陶
+伐
+餵
+療
+瓶
+婆
+撫
+臂
+摸
+忍
+蝦
+蠟
+鄰
+胸
+鞏
+擠
+偶
+棄
+槽
+勁
+乳
+鄧
+吉
+仁
+爛
+磚
+租
+烏
+艦
+伴
+瓜
+淺
+丙
+暫
+燥
+橡
+柳
+迷
+暖
+牌
+秧
+膽
+詳
+簧
+踏
+瓷
+譜
+呆
+賓
+糊
+洛
+輝
+憤
+競
+隙
+怒
+粘
+乃
+緒
+肩
+籍
+敏
+塗
+熙
+皆
+偵
+懸
+掘
+享
+糾
+醒
+狂
+鎖
+淀
+恨
+牲
+霸
+爬
+賞
+逆
+玩
+陵
+祝
+秒
+浙
+貌
+役
+彼
+悉
+鴨
+趨
+鳳
+晨
+畜
+輩
+秩
+卵
+署
+梯
+炎
+灘
+棋
+驅
+篩
+峽
+冒
+啥
+壽
+譯
+浸
+泉
+帽
+遲
+矽
+疆
+貸
+漏
+稿
+冠
+嫩
+脅
+芯
+牢
+叛
+蝕
+奧
+鳴
+嶺
+羊
+憑
+串
+塘
+繪
+酵
+融
+盆
+錫
+廟
+籌
+凍
+輔
+攝
+襲
+筋
+拒
+僚
+旱
+鉀
+鳥
+漆
+沈
+眉
+疏
+添
+棒
+穗
+硝
+韓
+逼
+扭
+僑
+涼
+挺
+碗
+栽
+炒
+杯
+患
+餾
+勸
+豪
+遼
+勃
+鴻
+旦
+吏
+拜
+狗
+埋
+輥
+掩
+飲
+搬
+罵
+辭
+勾
+扣
+估
+蔣
+絨
+霧
+丈
+朵
+姆
+擬
+宇
+輯
+陝
+雕
+償
+蓄
+崇
+剪
+倡
+廳
+咬
+駛
+薯
+刷
+斥
+番
+賦
+奉
+佛
+澆
+漫
+曼
+扇
+鈣
+桃
+扶
+仔
+返
+俗
+虧
+腔
+鞋
+棱
+覆
+框
+悄
+叔
+撞
+騙
+勘
+旺
+沸
+孤
+吐
+孟
+渠
+屈
+疾
+妙
+惜
+仰
+狠
+脹
+諧
+拋
+黴
+桑
+崗
+嘛
+衰
+盜
+滲
+臟
+賴
+湧
+甜
+曹
+閱
+肌
+哩
+厲
+烴
+緯
+毅
+昨
+偽
+症
+煮
+嘆
+釘
+搭
+莖
+籠
+酷
+偷
+弓
+錐
+恆
+傑
+坑
+鼻
+翼
+綸
+敘
+獄
+逮
+罐
+絡
+棚
+抑
+膨
+蔬
+寺
+驟
+穆
+冶
+枯
+冊
+屍
+凸
+紳
+坯
+犧
+焰
+轟
+欣
+晉
+瘦
+禦
+錠
+錦
+喪
+旬
+鍛
+壟
+搜
+撲
+邀
+亭
+酯
+邁
+舒
+脆
+酶
+閒
+憂
+酚
+頑
+羽
+漲
+卸
+仗
+陪
+闢
+懲
+杭
+姚
+肚
+捉
+飄
+漂
+昆
+欺
+吾
+郎
+烷
+汁
+呵
+飾
+蕭
+雅
+郵
+遷
+燕
+撒
+姻
+赴
+宴
+煩
+債
+帳
+斑
+鈴
+旨
+醇
+董
+餅
+雛
+姿
+拌
+傅
+腹
+妥
+揉
+賢
+拆
+歪
+葡
+胺
+丟
+浩
+徽
+昂
+墊
+擋
+覽
+貪
+慰
+繳
+汪
+慌
+馮
+諾
+姜
+誼
+兇
+劣
+誣
+耀
+昏
+躺
+盈
+騎
+喬
+溪
+叢
+盧
+抹
+悶
+諮
+刮
+駕
+纜
+悟
+摘
+鉺
+擲
+頗
+幻
+柄
+惠
+慘
+佳
+仇
+臘
+窩
+滌
+劍
+瞧
+堡
+潑
+蔥
+罩
+霍
+撈
+胎
+蒼
+濱
+倆
+捅
+湘
+砍
+霞
+邵
+萄
+瘋
+淮
+遂
+熊
+糞
+烘
+宿
+檔
+戈
+駁
+嫂
+裕
+徙
+箭
+捐
+腸
+撐
+曬
+辨
+殿
+蓮
+攤
+攪
+醬
+屏
+疫
+哀
+蔡
+堵
+沫
+皺
+暢
+疊
+閣
+萊
+敲
+轄
+鉤
+痕
+壩
+巷
+餓
+禍
+丘
+玄
+溜
+曰
+邏
+彭
+嘗
+卿
+妨
+艇
+吞
+韋
+怨
+矮
+歇
diff --git a/internal/html/assets/docs.html b/internal/html/assets/docs.html
index af5e9e5..dbf510e 100644
--- a/internal/html/assets/docs.html
+++ b/internal/html/assets/docs.html
@@ -855,7 +855,7 @@
Recovery in Anonymous Mode
Advanced: Multilingual Bundles
- Each person can receive their bundle in their preferred language. Six languages are supported: English, Spanish, German, French, Slovenian and Portuguese.
+ Each person can receive their bundle in their preferred language. Seven languages are supported: English, Spanish, German, French, Slovenian, Portuguese and Chinese (Taiwan).
How It Works
diff --git a/internal/html/assets/maker.html b/internal/html/assets/maker.html
index 8e47c3f..ef8d40f 100644
--- a/internal/html/assets/maker.html
+++ b/internal/html/assets/maker.html
@@ -280,6 +280,7 @@
FR
SL
PT
+ ZH-TW
@@ -461,8 +462,8 @@ 3 Generate Bu
// Set initial language immediately
(function() {
const saved = localStorage.getItem('rememory-lang');
- const browserLang = navigator.language.split('-')[0];
- currentLang = saved || (['es', 'de', 'fr', 'pt'].includes(browserLang) ? browserLang : 'en');
+ const detected = navigator.languages.find((language) => ['es', 'de', 'fr', 'sl', 'pt', 'zh-TW'].includes(language));
+ currentLang = saved || detected || 'en';
})();
// Initialize language toggle buttons after DOM is ready
diff --git a/internal/html/assets/recover.html b/internal/html/assets/recover.html
index abf60cd..4741067 100644
--- a/internal/html/assets/recover.html
+++ b/internal/html/assets/recover.html
@@ -51,6 +51,7 @@
FR
SL
PT
+ ZH-TW
@@ -185,8 +186,8 @@ 3 Recover the
// Set initial language immediately (before app.js runs)
(function() {
const saved = localStorage.getItem('rememory-lang');
- const browserLang = navigator.language.split('-')[0];
- currentLang = saved || (['es', 'de', 'fr', 'sl', 'pt'].includes(browserLang) ? browserLang : 'en');
+ const detected = navigator.languages.find((language) => ['es', 'de', 'fr', 'sl', 'pt', 'zh-TW'].includes(language));
+ currentLang = saved || detected || 'en';
})();
// Initialize language toggle buttons after DOM is ready
diff --git a/internal/html/assets/src/create-app.ts b/internal/html/assets/src/create-app.ts
index b6a4594..bea545f 100644
--- a/internal/html/assets/src/create-app.ts
+++ b/internal/html/assets/src/create-app.ts
@@ -323,7 +323,8 @@ declare let currentLang: string;
{ code: 'de', label: 'Deutsch' },
{ code: 'fr', label: 'Français' },
{ code: 'sl', label: 'Slovenščina' },
- { code: 'pt', label: 'Português' }
+ { code: 'pt', label: 'Português' },
+ { code: 'zh-TW', label: '正體中文' }
];
const langOptionsHtml = langOptions.map(o =>
`${escapeHtml(o.label)} `
@@ -409,7 +410,7 @@ declare let currentLang: string;
for (let k = 2; k <= n; k++) {
const option = document.createElement('option');
option.value = String(k);
- option.textContent = `${k} of ${n}`;
+ option.textContent = t('threshold_option', k, n);
elements.thresholdSelect.appendChild(option);
}
diff --git a/internal/project/project.go b/internal/project/project.go
index 7ef52f0..76e932b 100644
--- a/internal/project/project.go
+++ b/internal/project/project.go
@@ -20,7 +20,7 @@ const (
type Friend struct {
Name string `yaml:"name"`
Contact string `yaml:"contact,omitempty"`
- Language string `yaml:"language,omitempty"` // Bundle language override (e.g. "en", "es", "de", "fr", "sl", "pt")
+ Language string `yaml:"language,omitempty"` // Bundle language override (e.g. "en", "es", "de", "fr", "sl", "pt", "zh-TW")
}
// ShareInfo stores information about a generated share.
@@ -44,7 +44,7 @@ type Project struct {
Created string `yaml:"created"`
Threshold int `yaml:"threshold"`
Anonymous bool `yaml:"anonymous,omitempty"`
- Language string `yaml:"language,omitempty"` // Default bundle language (e.g. "en", "es", "de", "fr", "sl", "pt")
+ Language string `yaml:"language,omitempty"` // Default bundle language (e.g. "en", "es", "de", "fr", "sl", "pt", "zh-TW")
Friends []Friend `yaml:"friends"`
Sealed *Sealed `yaml:"sealed,omitempty"`
diff --git a/internal/translations/maker/de.json b/internal/translations/maker/de.json
index aae8827..7143aca 100644
--- a/internal/translations/maker/de.json
+++ b/internal/translations/maker/de.json
@@ -11,6 +11,7 @@
"num_shares_label": "Anzahl der Teile:",
"add_friend": "Freund hinzufügen",
"threshold_label": "Schwelle:",
+ "threshold_option": "{0} of {1}",
"threshold_desc": "müssen zustimmen, um wiederherzustellen",
"name_label": "Name",
"contact_label": "Kontakt (optional)",
diff --git a/internal/translations/maker/en.json b/internal/translations/maker/en.json
index 32c442a..9ccf3bc 100644
--- a/internal/translations/maker/en.json
+++ b/internal/translations/maker/en.json
@@ -11,6 +11,7 @@
"num_shares_label": "Number of shares:",
"add_friend": "Add Friend",
"threshold_label": "Threshold:",
+ "threshold_option": "{0} of {1}",
"threshold_desc": "must agree to recover",
"name_label": "Name",
"contact_label": "Contact info (optional)",
diff --git a/internal/translations/maker/es.json b/internal/translations/maker/es.json
index 6fa8574..cb5db63 100644
--- a/internal/translations/maker/es.json
+++ b/internal/translations/maker/es.json
@@ -11,6 +11,7 @@
"num_shares_label": "Número de partes:",
"add_friend": "Agregar amigo",
"threshold_label": "Umbral:",
+ "threshold_option": "{0} of {1}",
"threshold_desc": "deben estar de acuerdo para recuperar",
"name_label": "Nombre",
"contact_label": "Contacto (opcional)",
diff --git a/internal/translations/maker/fr.json b/internal/translations/maker/fr.json
index 37904f6..089ef97 100644
--- a/internal/translations/maker/fr.json
+++ b/internal/translations/maker/fr.json
@@ -11,6 +11,7 @@
"num_shares_label": "Nombre de parts :",
"add_friend": "Ajouter un ami",
"threshold_label": "Seuil :",
+ "threshold_option": "{0} of {1}",
"threshold_desc": "doivent se mettre d'accord",
"name_label": "Nom",
"contact_label": "Contact (optionnel)",
diff --git a/internal/translations/maker/pt.json b/internal/translations/maker/pt.json
index 84ea82f..9e3f046 100644
--- a/internal/translations/maker/pt.json
+++ b/internal/translations/maker/pt.json
@@ -11,6 +11,7 @@
"num_shares_label": "Número de partes:",
"add_friend": "Adicionar Amigo",
"threshold_label": "Número mínimo de partes:",
+ "threshold_option": "{0} of {1}",
"threshold_desc": "amigos necessários para recuperar",
"name_label": "Nome",
"contact_label": "Informações de contato (opcional)",
diff --git a/internal/translations/maker/sl.json b/internal/translations/maker/sl.json
index 3df798f..f0a17d9 100644
--- a/internal/translations/maker/sl.json
+++ b/internal/translations/maker/sl.json
@@ -11,6 +11,7 @@
"num_shares_label": "Število delov:",
"add_friend": "Dodaj prijatelja",
"threshold_label": "Prag:",
+ "threshold_option": "{0} of {1}",
"threshold_desc": "morajo soglašati za obnovitev",
"name_label": "Ime",
"contact_label": "Kontakt (neobvezno)",
diff --git a/internal/translations/maker/zh-TW.json b/internal/translations/maker/zh-TW.json
new file mode 100644
index 0000000..98d83d5
--- /dev/null
+++ b/internal/translations/maker/zh-TW.json
@@ -0,0 +1,64 @@
+{
+ "loading": "載入中……",
+ "title": "ReMemory - 建立復原包",
+ "page_title": "建立復原包",
+ "page_description": "加入你的檔案、選擇你信任的人,設定幾個人同意才能復原。每個人都會拿到一個獨立的復原包。所有操作都在你的裝置裡完成,不會離開瀏覽器。",
+ "subtitle": "確保你的重要文件觸手可及,即使當你無法自己取得時。",
+ "step1_title": "朋友",
+ "friends_hint": "每位朋友都會保管你的一部分復原金鑰",
+ "anonymous_label": "匿名",
+ "anonymous_hint": "拿到復原包的人不會知道彼此是誰",
+ "num_shares_label": "份數:",
+ "add_friend": "新增朋友",
+ "threshold_label": "門檻:",
+ "threshold_option": "{0}/{1}",
+ "threshold_desc": "位朋友同意才會開始復原",
+ "name_label": "姓名",
+ "contact_label": "聯絡方式(選填)",
+ "import_summary": "從現有的 project.yml 匯入聯絡人",
+ "import_placeholder": "在這裡貼上你的 project.yml 內容……",
+ "import_btn": "匯入聯絡人",
+ "step2_title": "需要保護的檔案",
+ "files_drop": "拖放一個資料夾到這裡,或點擊以選擇檔案",
+ "files_hint": "這些檔案會先加密,再分割交給每位朋友",
+ "files_summary": "{0} 個檔案,共 {1}",
+ "step3_title": "產生復原包",
+ "generate_btn": "產生復原包",
+ "download_all_btn": "下載所有復原包",
+ "download_yaml_btn": "儲存 project.yml",
+ "works_offline": "可完全離線使用",
+ "need_help": "需要幫助?",
+ "download_cli": "從 GitHub 下載命令列工具",
+ "generating": "產生中……",
+ "archiving": "封存中……",
+ "encrypting": "加密中……",
+ "splitting": "分割金鑰……",
+
+ "complete": "所有復原包已準備好。",
+ "error": "錯誤:{0}",
+ "validation_min_friends": "需要至少 2 位朋友",
+ "validation_friend_name": "朋友 {0}:請提供姓名",
+ "validation_no_files": "請選擇至少 1 個檔案",
+ "download": "下載",
+ "bundle_for": "{0} 的復原包",
+ "import_success": "已匯入 {0} 個聯絡人",
+ "import_error": "解析 YAML 失敗:{0}",
+ "error_title": "出了點問題",
+ "error_not_ready_title": "還沒準備好",
+ "error_not_ready_message": "復原包建立工具還在載入中。",
+ "error_not_ready_guidance": "請稍等片刻,然後重試。",
+ "error_import_title": "匯入失敗",
+ "error_import_guidance": "請檢查 YAML 的格式、縮排正確,並確保每位朋友都有填寫姓名。",
+ "error_min_friends_title": "朋友人數不足",
+ "error_min_friends_guidance": "由於需要分割成至少 2 份,請加入更多朋友以繼續。",
+ "error_generate_title": "復原包建立失敗",
+ "error_generate_guidance": "請確認每位朋友都有姓名,並且已經加入至少 1 個檔案。",
+ "action_try_again": "再試一次",
+ "validation_title": "有些欄位需要填寫",
+ "validation_message": "請先填寫所有已標示的欄位,再開始產生復原包。",
+ "validation_guidance": "每位朋友都需要填寫姓名,並且需要加入至少 1 個檔案。",
+ "language_label": "復原包語言",
+
+ "custom_language_label": "自訂語言",
+ "remove": "移除"
+}
diff --git a/internal/translations/readme/de.json b/internal/translations/readme/de.json
index a52637a..756bb43 100644
--- a/internal/translations/readme/de.json
+++ b/internal/translations/readme/de.json
@@ -56,6 +56,7 @@
"lang_de": "Deutsch",
"lang_sl": "Slowenisch",
"lang_pt": "Portugiesisch (Brasilien)",
+ "lang_zh-TW": "Chinesisch (Taiwan)",
"machine_readable": "MASCHINENLESBARES FORMAT (auf der Webseite einfügen):",
"metadata_footer": "METADATEN-FUSSZEILE (maschinenlesbar)",
"qr_caption": "Scanne mit deiner Handykamera, um deinen Teil zu importieren",
diff --git a/internal/translations/readme/en.json b/internal/translations/readme/en.json
index 554c78f..1110e94 100644
--- a/internal/translations/readme/en.json
+++ b/internal/translations/readme/en.json
@@ -56,6 +56,7 @@
"lang_de": "German",
"lang_sl": "Slovenian",
"lang_pt": "Portuguese (Brazil)",
+ "lang_zh-TW": "Chinese (Taiwan)",
"machine_readable": "MACHINE-READABLE FORMAT (paste on website):",
"metadata_footer": "METADATA FOOTER (machine-parseable)",
"qr_caption": "Scan with your phone camera to import your share",
diff --git a/internal/translations/readme/es.json b/internal/translations/readme/es.json
index 6a7e2d6..4f44ac2 100644
--- a/internal/translations/readme/es.json
+++ b/internal/translations/readme/es.json
@@ -56,6 +56,7 @@
"lang_de": "alemán",
"lang_sl": "esloveno",
"lang_pt": "Portugués (Brasil)",
+ "lang_zh-TW": "Chino (Taiwán)",
"machine_readable": "FORMATO DE COMPUTADOR (pega esto):",
"metadata_footer": "PIE DE METADATOS (parseable por máquina)",
"qr_caption": "Escanea con la cámara de tu teléfono para importar tu parte",
diff --git a/internal/translations/readme/fr.json b/internal/translations/readme/fr.json
index 9e803f5..a8e86ac 100644
--- a/internal/translations/readme/fr.json
+++ b/internal/translations/readme/fr.json
@@ -56,6 +56,7 @@
"lang_de": "allemand",
"lang_sl": "slovène",
"lang_pt": "Portugais (Brésil)",
+ "lang_zh-TW": "Chinois (Taïwan)",
"machine_readable": "FORMAT LISIBLE PAR MACHINE (collez sur le site web) :",
"metadata_footer": "PIED DE PAGE MÉTADONNÉES (analysable par machine)",
"qr_caption": "Scannez avec l'appareil photo de votre téléphone pour importer votre part",
diff --git a/internal/translations/readme/pt.json b/internal/translations/readme/pt.json
index 10ad37b..cb67d59 100644
--- a/internal/translations/readme/pt.json
+++ b/internal/translations/readme/pt.json
@@ -56,6 +56,7 @@
"lang_de": "Alemão",
"lang_sl": "Esloveno",
"lang_pt": "Português (Brasil)",
+ "lang_zh-TW": "Chinês (Taiwan)",
"machine_readable": "FORMATO LÍGIVEL POR MÁQUINA (cole no site):",
"metadata_footer": "RODAPÉ DE METADADOS (legivel por máquina)",
"qr_caption": "Escaneie isso com a câmera do seu telefone para importar sua parte",
diff --git a/internal/translations/readme/sl.json b/internal/translations/readme/sl.json
index 7e4107d..8b6eb51 100644
--- a/internal/translations/readme/sl.json
+++ b/internal/translations/readme/sl.json
@@ -56,6 +56,7 @@
"lang_de": "nemščina",
"lang_sl": "slovenščina",
"lang_pt": "Portugalščina (Brazilija)",
+ "lang_zh-TW": "kitajščina (Tajvan)",
"machine_readable": "STROJNO BERLJIV FORMAT (prilepite na spletno stran):",
"metadata_footer": "NOGA Z METAPODATKI (strojno razčlenljiva)",
"qr_caption": "Skenirajte s kamero telefona za uvoz vašega dela",
diff --git a/internal/translations/readme/zh-TW.json b/internal/translations/readme/zh-TW.json
new file mode 100644
index 0000000..73ff08e
--- /dev/null
+++ b/internal/translations/readme/zh-TW.json
@@ -0,0 +1,66 @@
+{
+ "title": "REMEMORY 復原包",
+ "for": "持有人:{0}",
+ "warning_cannot_alone": "這個檔案不能單獨使用",
+ "warning_need_friends": "你將需要下列持有人的協助。",
+ "warning_need_shares": "你需要把這個檔案和其他人手上的金鑰片段放在一起使用。",
+ "warning_confidential": "機密:請勿分享本文件",
+ "warning_keep_safe": "本文件含有你持有的金鑰片段,請妥善保管。",
+ "what_is_this": "這是什麼?",
+ "what_bundle_for": "這個復原包讓你能協助解鎖「{0}」的檔案。",
+ "what_one_of": "你是 {0} 位被託付這些金鑰片段的人之一。",
+ "what_threshold": "你們需要至少 {0} 位合作以解鎖檔案。",
+ "other_holders": "其他金鑰片段持有人(請聯絡以配合復原)",
+ "contact_label": "聯絡方式:{0}",
+ "sharing_title": "有人要求我的金鑰片段,我應該怎樣做?",
+ "sharing_verify": "首先,請確認要求是真實的。如果可以的話,自己聯絡原始檔案的擁有者進一步確認。",
+ "sharing_easiest": "提供協助最簡單的方法是傳送整個 ZIP 檔給他們。",
+ "sharing_readme_only": "如果你無法傳送 ZIP 檔,他們只需要你的 README.txt(這個檔案)。",
+ "sharing_words_phone": "如果你無法傳送任何檔案,可透過電話讀出下列復原詞組。",
+ "sharing_qr_mail": "下面的 QR 碼也可以列印出來,以郵寄方式送出。",
+ "recover_browser": "如何復原(主要方式:瀏覽器)",
+ "recover_step1": "1. 使用任何現代瀏覽器(Chrome、Firefox、Safari、Edge)開啟 recover.html",
+ "recover_share_loaded": "本復原包的解鎖工具專為你設計,你保管的金鑰片段已被預先載入。",
+ "recover_no_html": "如果你沒有 recover.html,請瀏覽 https://eljojo.github.io/rememory/recover",
+ "recover_step2": "2. 由本復原包載入加密封存檔(MANIFEST.age):",
+ "recover_step2_drag": "- 拖放到封存檔區域;或",
+ "recover_step2_click": "- 點擊以瀏覽並選擇封存檔",
+ "recover_step2_embedded": "2. 加密封存檔已經預先載入,無須再手動載入。",
+ "recover_step2_embedded_hint": "如果你用的是別的復原工具,請把這個復原包裡的 recover.html 拖放到那個工具裡。",
+ "recover_step3_contact": "3. 你會看到一份聯絡人清單,列出其他金鑰片段持有人",
+ "recover_step3_ask": "聯絡並請求他們傳送他們的 README.txt 給你",
+ "recover_step4": "4. 當你收到他們的 README.txt:",
+ "recover_step4_drag": "- 拖放到網頁;或",
+ "recover_step4_paste": "- 點擊剪貼簿按鈕以貼上他們的金鑰片段",
+ "recover_step5_checkmarks": "5. 當你加入金鑰片段,對應持有人的姓名旁邊會標上 ✅",
+ "recover_step5_auto": "當你收集了 {0} 份金鑰片段,復原程式會自動開始",
+ "recover_step6": "6. 下載已復原的檔案",
+ "recover_anon_step3": "3. 加入你收集到的其他金鑰片段",
+ "recover_anon_step3_drag": "- 拖放 README.txt 到網頁;或",
+ "recover_anon_step3_paste": "- 點擊剪貼簿按鈕以貼上金鑰片段",
+ "recover_anon_step4_auto": "4. 當你收集到 {0} 份金鑰片段,復原程序會自動開始",
+ "recover_anon_step5": "5. 下載已復原的檔案",
+ "recover_offline": "可完全離線使用,無須網路。",
+ "recover_cli": "如何復原(後備方式:命令列)",
+ "recover_cli_hint": "如果 recover.html 無法運作,下載命令列工具:",
+ "recover_cli_usage": "用法:rememory recover share1.txt share2.txt ... --manifest recover.html",
+ "your_share": "你的金鑰片段",
+ "recovery_words_title": "你的 {0} 個復原詞組:",
+ "recovery_words_title_lang": "你的 {0} 個復原詞組({1}):",
+ "recovery_words_title_english": "你的 {0} 個復原詞組(英文):",
+ "recovery_words_hint": "向負責復原的人讀出這些字詞,或輸入到 recover.html 的復原工具。",
+ "recovery_words_dual_hint": "不同語言的詞組清單編碼相同的資料,任一均可用於復原檔案。",
+ "lang_en": "英文",
+ "lang_es": "西班牙文",
+ "lang_fr": "法文",
+ "lang_de": "德文",
+ "lang_sl": "斯洛維尼亞文",
+ "lang_pt": "葡萄牙語(巴西)",
+ "lang_zh-TW": "正體中文",
+ "machine_readable": "機器可讀格式(貼到網頁上):",
+ "metadata_footer": "中繼資料註腳(機器可解析)",
+ "qr_caption": "掃描以匯入金鑰片段",
+ "recovery_rule": "復原條件",
+ "recovery_rule_count": "需要 {0}/{1} 位持有人",
+ "readme_filename": "README"
+}
diff --git a/internal/translations/recover/zh-TW.json b/internal/translations/recover/zh-TW.json
new file mode 100644
index 0000000..aa9af15
--- /dev/null
+++ b/internal/translations/recover/zh-TW.json
@@ -0,0 +1,89 @@
+{
+ "loading": "載入中……",
+ "title": "復原檔案",
+ "subtitle": "將朋友妥善保管的金鑰片段收集起來。",
+ "page_description": "每位朋友都有收到一個含有一部分復原金鑰的復原包。收集足夠的金鑰片段、加入加密封存檔,然後你的檔案會在瀏覽器解鎖。所有資料都不會離開你的裝置。",
+ "step1_title": "收集金鑰片段",
+ "step1_drop": "拖放 README.txt 到這裡,或點擊以選擇檔案",
+ "step1_hint": "每個文件含有一個人持有的金鑰片段",
+ "step2_title": "加入加密封存檔",
+ "step2_drop": "拖放 recover.html 或 MANIFEST.age 到這裡,或點擊以選擇檔案",
+ "step2_hint": "使用任何一位朋友的復原包裡的 recover.html 或 MANIFEST.age",
+ "step3_title": "復原檔案",
+ "decrypt_btn": "解鎖及復原",
+ "download_btn": "下載封存檔(.tar.gz)",
+ "no_manifest": "未加入封存檔",
+ "works_offline": "可完全離線使用",
+ "need_help": "需要幫助?",
+ "download_cli": "從 GitHub 下載命令列工具",
+ "need_more": "還需 {0} 個金鑰片段",
+ "need_more_one": "還需最後一個金鑰片段",
+ "ready": "一切準備就緒",
+ "shares_of": "{1} 之 {0} 個",
+ "remove": "移除",
+ "loaded": "已載入",
+ "manifest_loaded_bundle": "已從復原包載入",
+ "manifest_loaded_embedded": "已預先載入",
+ "manifest_loaded_html": "已從 recover.html 抽出",
+ "combining": "正在合併金鑰片段……",
+ "decrypting": "解鎖中……",
+ "reading": "正在開啟封存檔……",
+ "complete": "完成。已復原 {0} 個檔案。",
+ "error": "錯誤:{0}",
+ "paste_btn": "貼上金鑰片段或輸入復原詞組",
+ "paste_placeholder": "貼上收到的文字或輸入復原詞組……",
+ "paste_submit": "加入金鑰片段",
+ "your_share": "你的金鑰片段",
+ "contact_list": "聯絡其他人",
+ "contact_list_hint": "聯絡這些朋友,請他們幫忙提供金鑰片段",
+ "pasted_content": "貼上的文字",
+ "scan_btn": "掃描 QR 碼",
+ "scan_title": "掃描 QR 碼",
+ "scan_hint": "將你的鏡頭指向朋友 PDF 的 QR 碼",
+ "scan_camera_error": "無法使用攝影機",
+ "error_invalid_words_title": "復原詞組無效",
+ "error_invalid_words_guidance": "請檢查詞組是否有錯字,每個字詞應該跟復原指引中列出的一致。",
+ "error_title": "出了點問題",
+ "error_wasm_title": "無法載入復原工具",
+ "error_wasm_message": "復原模組無法被載入。",
+ "error_wasm_guidance": "請嘗試重新載入網頁,或使用其他瀏覽器(Chrome 或 Firefox)。你也可以使用命令列工具。",
+ "error_not_ready_title": "還沒準備好",
+ "error_not_ready_message": "復原工具還在載入中。",
+ "error_not_ready_guidance": "請稍等片刻,然後重試。",
+ "error_invalid_share_title": "金鑰片段格式無效",
+ "error_invalid_share_message": "檔案「{0}」不包含有效的金鑰片段。",
+ "error_invalid_share_guidance": "請使用復原包的 README.txt。檔案中應包含被「BEGIN REMEMORY SHARE」及「END REMEMORY SHARE」標記包圍的文字段。",
+ "error_no_share_title": "找不到金鑰片段",
+ "error_no_share_message": "檔案「{0}」不包含金鑰片段。",
+ "error_no_share_guidance": "每位朋友都有收到復原包,當中的 README.txt 含有他的金鑰片段。請檢查你的檔案是否正確。",
+ "error_duplicate_title": "重複的金鑰片段",
+ "error_duplicate_message": "第 {0} 個金鑰片段已被加入。",
+ "error_duplicate_guidance": "每個金鑰片段只能被使用一次,請加入其他朋友的金鑰片段。",
+ "error_file_read_title": "無法讀取檔案",
+ "error_file_read_message": "無法讀取檔案「{0}」。",
+ "error_file_read_guidance": "檔案可能已損壞或無法讀取,請嘗試再次下載或要求你的朋友再次傳送他們的復原包。",
+ "error_bundle_extract_title": "復原包無效",
+ "error_bundle_extract_message": "無法解壓縮復原包「{0}」。",
+ "error_bundle_extract_guidance": "這個 ZIP 檔案似乎不是有效的 ReMemory 復原包,請使用最初交給你的 bundle.zip。",
+ "error_wrong_manifest_title": "錯誤的檔案類型",
+ "error_wrong_manifest_message": "檔案「{0}」不是加密封存檔。",
+ "error_wrong_manifest_guidance": "請拖放任一朋友保管的復原包的 recover.html 或 MANIFEST.age。",
+ "error_html_no_manifest_guidance": "這個 recover.html 沒有嵌入加密封存檔,請嘗試使用其他朋友復原包中的 recover.html 或使用 MANIFEST.age。",
+ "error_paste_no_share_title": "貼上的文字沒有金鑰片段",
+ "error_paste_no_share_message": "貼上的文字不含有效的金鑰片段。",
+ "error_paste_no_share_guidance": "請從朋友的 README.txt 貼上完整內容,包括「BEGIN REMEMORY SHARE」及「END REMEMORY SHARE」標記。你也可以輸入或貼上復原詞組。",
+ "error_decrypt_title": "解鎖失敗",
+ "error_decrypt_message": "封存檔無法由提供的金鑰片段解鎖。",
+ "error_decrypt_guidance": "金鑰片段可能數量不足或不是用於這個封存檔,請確保所有金鑰片段都屬於這個封存檔。",
+ "error_decrypt_status": "解鎖失敗,請檢查你的金鑰片段並再試一次。",
+ "error_extract_title": "封存檔解壓縮失敗",
+ "error_extract_message": "無法開啟已解鎖的封存檔。",
+ "error_extract_guidance": "封存檔可能已損壞。如果你有 MANIFEST.age 的其他複本,請使用其再試一次。",
+ "error_extract_status": "解壓縮失敗,封存檔可能已損壞。",
+ "error_recovery_title": "復原失敗",
+ "error_recovery_guidance": "請檢查你有正確的金鑰片段及 MANIFEST.age。你可以用不同的金鑰片段再嘗試一次。",
+ "action_reload": "重新載入網頁",
+ "action_use_cli": "使用命令列工具",
+ "action_try_again": "再試一次",
+ "action_try_different_shares": "嘗試不同的金鑰片段"
+}
diff --git a/internal/translations/translations.go b/internal/translations/translations.go
index 9a3b782..76ea879 100644
--- a/internal/translations/translations.go
+++ b/internal/translations/translations.go
@@ -18,7 +18,7 @@ var makerFS embed.FS
var readmeFS embed.FS
// Languages lists all supported language codes.
-var Languages = []string{"en", "es", "de", "fr", "sl", "pt"}
+var Languages = []string{"en", "es", "de", "fr", "sl", "pt", "zh-TW"}
// GetTranslationsJS builds the JavaScript translations object for injection into HTML templates.
// component must be "recover", "maker", or "readme".
@@ -53,7 +53,8 @@ func GetTranslationsJS(component string) string {
valJSON, _ := json.Marshal(check[k])
entries = append(entries, fmt.Sprintf(" %s: %s", string(keyJSON), string(valJSON)))
}
- parts = append(parts, fmt.Sprintf(" %s: {\n%s\n }", lang, strings.Join(entries, ",\n")))
+ langJSON, _ := json.Marshal(lang)
+ parts = append(parts, fmt.Sprintf(" %s: {\n%s\n }", langJSON, strings.Join(entries, ",\n")))
}
return "{\n" + strings.Join(parts, ",\n\n") + "\n }"
diff --git a/internal/translations/translations_test.go b/internal/translations/translations_test.go
index e5ffc01..5855b24 100644
--- a/internal/translations/translations_test.go
+++ b/internal/translations/translations_test.go
@@ -81,7 +81,7 @@ func TestGetTranslationsJSProducesValidJS(t *testing.T) {
}
for _, lang := range Languages {
- if !strings.Contains(js, lang+": {") {
+ if !strings.Contains(js, "\""+lang+"\": {") {
t.Errorf("GetTranslationsJS(%s) missing language %s", component, lang)
}
}