Line data Source code
1 : // Package util は i18n 関連の共通ヘルパー関数を提供します。
2 : // 旧 shared/i18n/loader.go から移植された wrapWithPath, deepMerge, flatten などを含みます。
3 : package util
4 :
5 : import (
6 : "fmt"
7 : "os"
8 : "strings"
9 :
10 : "gopkg.in/yaml.v3"
11 : )
12 :
13 : // ReadYAMLFile はファイルパスを受け取り、YAML を map[string]any にパースします。
14 0 : func ReadYAMLFile(path string) (map[string]any, error) {
15 0 : data, err := os.ReadFile(path)
16 0 : if err != nil {
17 0 : return nil, fmt.Errorf("read file: %w", err)
18 0 : }
19 0 : out := map[string]any{}
20 0 : if err := yaml.Unmarshal(data, &out); err != nil {
21 0 : return nil, fmt.Errorf("unmarshal yaml: %w", err)
22 0 : }
23 0 : return out, nil
24 : }
25 :
26 : // WrapWithPath は ["ui","page","home"] + {"title":"Home"} を
27 : // {"ui":{"page":{"home":{"title":"Home"}}}} の形に変換します。
28 0 : func WrapWithPath(prefix []string, in map[string]any) map[string]any {
29 0 : if len(prefix) == 0 {
30 0 : return in
31 0 : }
32 0 : last := prefix[len(prefix)-1]
33 0 : if len(prefix) == 1 {
34 0 : return map[string]any{last: in}
35 0 : }
36 0 : return map[string]any{prefix[0]: WrapWithPath(prefix[1:], in)}
37 : }
38 :
39 : // DeepMerge は src の内容を dst に再帰的にマージします。
40 : // ネスト構造が衝突する場合、map[string]any 同士なら再帰的に統合します。
41 0 : func DeepMerge(dst, src map[string]any) error {
42 0 : for k, v := range src {
43 0 : if dv, ok := dst[k]; ok {
44 0 : // 両方 map[string]any の場合のみ再帰マージ
45 0 : dm, dok := dv.(map[string]any)
46 0 : sm, sok := v.(map[string]any)
47 0 : if dok && sok {
48 0 : if err := DeepMerge(dm, sm); err != nil {
49 0 : return err
50 0 : }
51 0 : continue
52 : }
53 : }
54 0 : dst[k] = v
55 : }
56 0 : return nil
57 : }
58 :
59 : // Flatten はネスト構造を "a.b.c" 形式のキーでフラット化します。
60 0 : func Flatten(prefix string, in map[string]any, out map[string]any) {
61 0 : for k, v := range in {
62 0 : key := k
63 0 : if prefix != "" {
64 0 : key = prefix + "." + k
65 0 : }
66 0 : switch vv := v.(type) {
67 0 : case map[string]any:
68 0 : Flatten(key, vv, out)
69 0 : case map[any]any: // yaml.v2系がこれを返すことがある
70 0 : m2 := make(map[string]any, len(vv))
71 0 : for kk, vv2 := range vv {
72 0 : if s, ok := kk.(string); ok {
73 0 : m2[s] = vv2
74 0 : }
75 : }
76 0 : Flatten(key, m2, out)
77 0 : default:
78 0 : out[key] = vv
79 : }
80 : }
81 : }
82 :
83 : // DetectLocale はパスからロケールコード (ja, en, etc.) を抽出します。
84 0 : func DetectLocale(path string) string {
85 0 : normalized := strings.ReplaceAll(path, `\`, "/")
86 0 : segs := strings.Split(normalized, "/")
87 0 : for _, cand := range []string{"ja", "en"} {
88 0 : for _, s := range segs {
89 0 : if s == cand {
90 0 : return cand
91 0 : }
92 : }
93 : }
94 0 : return "ja"
95 : }
|