package handlers import ( "net/http" "regexp" "strings" ) const postI18nColsSQL = ` COALESCE(p.text_zh,''), COALESCE(p.text_en,''), COALESCE(p.text_ja,''), COALESCE(p.text_ko,''), COALESCE(p.text_vi,''), COALESCE(p.text_id,''), COALESCE(p.text_ms,'')` const maxPostTextLen = 32768 const maxPostAttachments = 20 var postLinkRe = regexp.MustCompile(`https?://`) type postTextI18n struct { TextZh, TextEn, TextJa, TextKo, TextVi, TextId, TextMs string } type postLocalePayload struct { Text string `json:"text"` } // requestLangCode is the normalized locale from ?lang= or Accept-Language (zh, en, ja, …). func requestLangCode(r *http.Request) string { raw := strings.TrimSpace(r.URL.Query().Get("lang")) if raw == "" { raw = r.Header.Get("Accept-Language") } raw = strings.TrimSpace(strings.Split(raw, ",")[0]) if raw == "" { return "zh" } return translateNormalizePostLang(raw) } // textForLang returns body text stored for the post's source language only. func (t postTextI18n) textForLang(lang string) string { switch translateNormalizePostLang(lang) { case "en": return strings.TrimSpace(t.TextEn) case "ja": return strings.TrimSpace(t.TextJa) case "ko": return strings.TrimSpace(t.TextKo) case "vi": return strings.TrimSpace(t.TextVi) case "id": return strings.TrimSpace(t.TextId) case "ms": return strings.TrimSpace(t.TextMs) default: return strings.TrimSpace(t.TextZh) } } func postTextI18nFromSingle(lang, text string) postTextI18n { lang = translateNormalizePostLang(lang) text = strings.TrimSpace(text) var t postTextI18n switch lang { case "en": t.TextEn = text case "ja": t.TextJa = text case "ko": t.TextKo = text case "vi": t.TextVi = text case "id": t.TextId = text case "ms": t.TextMs = text default: t.TextZh = text } return t } func normalizePostSourceLang(lang string) string { lang = translateNormalizePostLang(strings.TrimSpace(lang)) switch lang { case "zh", "en", "ja", "ko", "vi", "id", "ms": return lang default: return "zh" } } func (t postTextI18n) anyNonEmpty() bool { return strings.TrimSpace(t.TextZh) != "" || strings.TrimSpace(t.TextEn) != "" || strings.TrimSpace(t.TextJa) != "" || strings.TrimSpace(t.TextKo) != "" || strings.TrimSpace(t.TextVi) != "" || strings.TrimSpace(t.TextId) != "" || strings.TrimSpace(t.TextMs) != "" } func (t postTextI18n) legacyPrimary() string { if strings.TrimSpace(t.TextZh) != "" { return strings.TrimSpace(t.TextZh) } if strings.TrimSpace(t.TextEn) != "" { return strings.TrimSpace(t.TextEn) } return "" } func scanPostTextI18n(zh, en, ja, ko, vi, id, ms string) postTextI18n { return postTextI18n{TextZh: zh, TextEn: en, TextJa: ja, TextKo: ko, TextVi: vi, TextId: id, TextMs: ms} } func (t postTextI18n) toLocalizations() map[string]postLocalePayload { return map[string]postLocalePayload{ "zh": {Text: t.TextZh}, "en": {Text: t.TextEn}, "ja": {Text: t.TextJa}, "ko": {Text: t.TextKo}, "vi": {Text: t.TextVi}, "id": {Text: t.TextId}, "ms": {Text: t.TextMs}, } } func postTextHasLink(text string) bool { return postLinkRe.MatchString(text) } func truncatePostTexts(t postTextI18n) postTextI18n { tr := func(s string) string { s = strings.TrimSpace(s) if len(s) > maxPostTextLen { return s[:maxPostTextLen] } return s } t.TextZh = tr(t.TextZh) t.TextEn = tr(t.TextEn) t.TextJa = tr(t.TextJa) t.TextKo = tr(t.TextKo) t.TextVi = tr(t.TextVi) t.TextId = tr(t.TextId) t.TextMs = tr(t.TextMs) return t }