Skip to content

Commit 5aeba6b

Browse files
committed
docs: add multilingual documentation with full Chinese localization
- Add language selector links to the documentation - Introduce comprehensive Simplified Chinese and Traditional Chinese README files - Improve accessibility for Chinese-speaking users by providing localized documentation Signed-off-by: appleboy <[email protected]>
1 parent dd0970b commit 5aeba6b

File tree

3 files changed

+724
-0
lines changed

3 files changed

+724
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# go-fcm
22

3+
[English](README.md) | [繁體中文](README.zh-tw.md) | [简体中文](README.zh-cn.md)
4+
35
[![GoDoc](https://pkg.go.dev/badge/github.com/appleboy/go-fcm)](https://pkg.go.dev/github.com/appleboy/go-fcm)
46
[![Lint and Testing](https://github.com/appleboy/go-fcm/actions/workflows/testing.yml/badge.svg?branch=master)](https://github.com/appleboy/go-fcm/actions/workflows/testing.yml)
57
[![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/go-fcm)](https://goreportcard.com/report/github.com/appleboy/go-fcm)

README.zh-cn.md

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
# go-fcm
2+
3+
[English](README.md) | [繁體中文](README.zh-tw.md) | [简体中文](README.zh-cn.md)
4+
5+
[![GoDoc](https://pkg.go.dev/badge/github.com/appleboy/go-fcm)](https://pkg.go.dev/github.com/appleboy/go-fcm)
6+
[![Lint and Testing](https://github.com/appleboy/go-fcm/actions/workflows/testing.yml/badge.svg?branch=master)](https://github.com/appleboy/go-fcm/actions/workflows/testing.yml)
7+
[![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/go-fcm)](https://goreportcard.com/report/github.com/appleboy/go-fcm)
8+
9+
> 基于 [github.com/edganiukov/fcm](https://github.com/edganiukov/fcm) 分支
10+
> [Firebase 云消息官方文档](https://firebase.google.com/docs/cloud-messaging/)
11+
12+
---
13+
14+
## 目录
15+
16+
- [go-fcm](#go-fcm)
17+
- [目录](#目录)
18+
- [功能特性](#功能特性)
19+
- [支持的消息类型](#支持的消息类型)
20+
- [快速开始](#快速开始)
21+
- [认证与凭证](#认证与凭证)
22+
- [使用示例](#使用示例)
23+
- [高级配置](#高级配置)
24+
- [自定义 HTTP Client](#自定义-http-client)
25+
- [代理支持](#代理支持)
26+
- [单元测试与模拟](#单元测试与模拟)
27+
- [最佳实践](#最佳实践)
28+
- [故障排查](#故障排查)
29+
- [架构图](#架构图)
30+
- [常见问题](#常见问题)
31+
- [许可证](#许可证)
32+
33+
---
34+
35+
## 功能特性
36+
37+
| 功能 | 支持 | 说明 |
38+
| ------------------ | :--: | ----------------------------- |
39+
| 单设备推送 || 发送消息到单一设备 |
40+
| 多设备推送 || 发送消息到多个设备 |
41+
| 主题推送 || 发送消息到指定主题 |
42+
| 条件推送 || 支持 FCM 条件语法 |
43+
| 自定义 HTTP Client || 自定义超时、代理、传输设置 |
44+
| 多种消息格式 || Data、Notification、Multicast |
45+
| 单元测试与模拟支持 || 便于使用 mock client 单元测试 |
46+
47+
---
48+
49+
## 支持的消息类型
50+
51+
| 类型 | 说明 |
52+
| ------------ | ----------------------------- |
53+
| Data | 自定义数据消息,由 App 处理 |
54+
| Notification | 系统通知消息,显示于通知栏 |
55+
| Multicast | 一次发送最多 500 个设备 token |
56+
| Topic | 发送给订阅指定主题的所有设备 |
57+
| Condition | 发送给符合逻辑条件的设备 |
58+
59+
---
60+
61+
## 快速开始
62+
63+
安装 go-fcm:
64+
65+
```bash
66+
go get github.com/appleboy/go-fcm
67+
```
68+
69+
---
70+
71+
## 认证与凭证
72+
73+
推荐使用 Google Application Default Credentials (ADC) 进行认证。
74+
请从 [Firebase 控制台 > 设置 > 服务账号][11] 下载 JSON 密钥,并设置环境变量:
75+
76+
```bash
77+
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/serviceAccountKey.json"
78+
```
79+
80+
也可以在代码中直接指定密钥路径。
81+
82+
[11]: https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk
83+
84+
---
85+
86+
## 使用示例
87+
88+
```go
89+
package main
90+
91+
import (
92+
"context"
93+
"fmt"
94+
"log"
95+
96+
"firebase.google.com/go/v4/messaging"
97+
fcm "github.com/appleboy/go-fcm"
98+
)
99+
100+
func main() {
101+
ctx := context.Background()
102+
client, err := fcm.NewClient(
103+
ctx,
104+
fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
105+
// fcm.WithServiceAccount("[email protected]"),
106+
)
107+
if err != nil {
108+
log.Fatal(err)
109+
}
110+
111+
// 发送到单一设备
112+
token := "YOUR_DEVICE_TOKEN"
113+
resp, err := client.Send(
114+
ctx,
115+
&messaging.Message{
116+
Token: token,
117+
Data: map[string]string{
118+
"foo": "bar",
119+
},
120+
},
121+
)
122+
if err != nil {
123+
log.Fatal(err)
124+
}
125+
fmt.Println("成功:", resp.SuccessCount, "失败:", resp.FailureCount)
126+
127+
// 发送到主题
128+
resp, err = client.Send(
129+
ctx,
130+
&messaging.Message{
131+
Data: map[string]string{
132+
"foo": "bar",
133+
},
134+
Topic: "highScores",
135+
},
136+
)
137+
if err != nil {
138+
log.Fatal(err)
139+
}
140+
141+
// 条件推送
142+
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"
143+
message := &messaging.Message{
144+
Data: map[string]string{
145+
"score": "850",
146+
"time": "2:45",
147+
},
148+
Condition: condition,
149+
}
150+
resp, err = client.Send(ctx, message)
151+
if err != nil {
152+
log.Fatal(err)
153+
}
154+
155+
// 多设备推送
156+
registrationToken := "YOUR_REGISTRATION_TOKEN"
157+
messages := []*messaging.Message{
158+
{
159+
Notification: &messaging.Notification{
160+
Title: "价格下跌",
161+
Body: "所有电子产品 5% 折扣",
162+
},
163+
Token: registrationToken,
164+
},
165+
{
166+
Notification: &messaging.Notification{
167+
Title: "价格下跌",
168+
Body: "所有书籍 2% 折扣",
169+
},
170+
Topic: "readers-club",
171+
},
172+
}
173+
resp, err = client.Send(ctx, messages...)
174+
if err != nil {
175+
log.Fatal(err)
176+
}
177+
178+
// Multicast 推送
179+
registrationTokens := []string{
180+
"YOUR_REGISTRATION_TOKEN_1",
181+
"YOUR_REGISTRATION_TOKEN_2",
182+
// ...
183+
}
184+
msg := &messaging.MulticastMessage{
185+
Data: map[string]string{
186+
"score": "850",
187+
"time": "2:45",
188+
},
189+
Tokens: registrationTokens,
190+
}
191+
resp, err = client.SendMulticast(ctx, msg)
192+
if err != nil {
193+
log.Fatal(err)
194+
}
195+
fmt.Printf("%d 条消息发送成功\n", resp.SuccessCount)
196+
if resp.FailureCount > 0 {
197+
var failedTokens []string
198+
for idx, resp := range resp.Responses {
199+
if !resp.Success {
200+
failedTokens = append(failedTokens, registrationTokens[idx])
201+
}
202+
}
203+
fmt.Printf("失败的 token 列表: %v\n", failedTokens)
204+
}
205+
}
206+
```
207+
208+
---
209+
210+
## 高级配置
211+
212+
### 自定义 HTTP Client
213+
214+
```go
215+
import (
216+
"crypto/tls"
217+
"net"
218+
"net/http"
219+
"time"
220+
"golang.org/x/net/http2"
221+
)
222+
223+
func main() {
224+
httpTimeout := 5 * time.Second
225+
tlsTimeout := 5 * time.Second
226+
227+
transport := &http2.Transport{
228+
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
229+
return tls.DialWithDialer(&net.Dialer{Timeout: tlsTimeout}, network, addr, cfg)
230+
},
231+
}
232+
233+
httpClient := &http.Client{
234+
Transport: transport,
235+
Timeout: httpTimeout,
236+
}
237+
238+
ctx := context.Background()
239+
client, err := fcm.NewClient(
240+
ctx,
241+
fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
242+
fcm.WithHTTPClient(httpClient),
243+
)
244+
}
245+
```
246+
247+
### 代理支持
248+
249+
```go
250+
func main() {
251+
ctx := context.Background()
252+
client, err := fcm.NewClient(
253+
ctx,
254+
fcm.WithCredentialsFile("path/to/serviceAccountKey.json"),
255+
fcm.WithHTTPProxy("http://localhost:8088"),
256+
)
257+
}
258+
```
259+
260+
### 单元测试与模拟
261+
262+
```go
263+
import (
264+
"context"
265+
"net/http"
266+
"net/http/httptest"
267+
"testing"
268+
269+
"firebase.google.com/go/v4/messaging"
270+
fcm "github.com/appleboy/go-fcm"
271+
"google.golang.org/api/option"
272+
)
273+
274+
func TestMockClient(t *testing.T) {
275+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
276+
w.WriteHeader(http.StatusOK)
277+
w.Header().Set("Content-Type", "application/json")
278+
_, _ = w.Write([]byte(`{"name": "q1w2e3r4"}`))
279+
}))
280+
defer server.Close()
281+
282+
client, err := fcm.NewClient(
283+
context.Background(),
284+
fcm.WithEndpoint(server.URL),
285+
fcm.WithProjectID("test"),
286+
fcm.WithCustomClientOption(option.WithoutAuthentication()),
287+
)
288+
if err != nil {
289+
t.Fatalf("发生意外错误: %v", err)
290+
}
291+
resp, err := client.Send(
292+
context.Background(),
293+
&messaging.Message{
294+
Token: "test",
295+
Data: map[string]string{
296+
"foo": "bar",
297+
},
298+
})
299+
if err != nil {
300+
t.Fatalf("发生意外错误: %v", err)
301+
}
302+
// 检查响应
303+
if resp.SuccessCount != 1 {
304+
t.Fatalf("预期 1 成功,实际: %d", resp.SuccessCount)
305+
}
306+
if resp.FailureCount != 0 {
307+
t.Fatalf("预期 0 失败,实际: %d", resp.FailureCount)
308+
}
309+
}
310+
```
311+
312+
---
313+
314+
## 最佳实践
315+
316+
> [!TIP]
317+
>
318+
> - 批量推送时,每批建议不超过 500 个 token。
319+
> - 推荐使用主题管理设备组,避免直接管理 token。
320+
> - 凭证文件请设为只读并妥善保存。
321+
322+
---
323+
324+
## 故障排查
325+
326+
| 错误代码 | 可能原因与解决方式 |
327+
| ------------------ | -------------------------------- |
328+
| `UNREGISTERED` | Token 无效或过期,请从数据库移除 |
329+
| `INVALID_ARGUMENT` | 消息格式错误,请检查 payload |
330+
| `QUOTA_EXCEEDED` | FCM 配额已满,请稍后再试 |
331+
| `UNAUTHORIZED` | 凭证无效,请检查密钥与访问权限 |
332+
| `INTERNAL` | FCM 服务器错误,请重试请求 |
333+
334+
---
335+
336+
## 架构图
337+
338+
```mermaid
339+
flowchart TD
340+
A[你的 Go 服务器] -->|go-fcm| B[FCM API]
341+
B --> C[Android/iOS/Web 设备]
342+
B -.-> D[主题/条件路由]
343+
```
344+
345+
---
346+
347+
## 常见问题
348+
349+
- 问:如何获取 FCM 设备 token?
350+
- 问:如何设置多语言通知?
351+
- 问:如何追踪消息投递状态?
352+
353+
更多请参见 [Firebase 官方 FAQ](https://firebase.google.com/support/faq/)
354+
355+
---
356+
357+
## 许可证
358+
359+
本项目采用 [MIT License](LICENSE) 授权。
360+
361+
---

0 commit comments

Comments
 (0)