mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Make better use of i18n (#20096)
* Prototyping * Start work on creating offsets * Modify tests * Start prototyping with actual MPH * Twiddle around * Twiddle around comments * Convert templates * Fix external languages * Fix latest translation * Fix some test * Tidy up code * Use simple map * go mod tidy * Move back to data structure - Uses less memory by creating for each language a map. * Apply suggestions from code review Co-authored-by: delvh <dev.lh@web.de> * Add some comments * Fix tests * Try to fix tests * Use en-US as defacto fallback * Use correct slices * refactor (#4) * Remove TryTr, add log for missing translation key * Refactor i18n - Separate dev and production locale stores. - Allow for live-reloading in dev mode. Co-authored-by: zeripath <art27@cantab.net> * Fix live-reloading & check for errors * Make linter happy * live-reload with periodic check (#5) * Fix tests Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		| @@ -12,7 +12,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/translation/i18n" | ||||
| 	"code.gitea.io/gitea/modules/translation" | ||||
| ) | ||||
|  | ||||
| // Seconds-based time units | ||||
| @@ -29,146 +29,146 @@ func round(s float64) int64 { | ||||
| 	return int64(math.Round(s)) | ||||
| } | ||||
|  | ||||
| func computeTimeDiffFloor(diff int64, lang string) (int64, string) { | ||||
| 	var diffStr string | ||||
| func computeTimeDiffFloor(diff int64, lang translation.Locale) (int64, string) { | ||||
| 	diffStr := "" | ||||
| 	switch { | ||||
| 	case diff <= 0: | ||||
| 		diff = 0 | ||||
| 		diffStr = i18n.Tr(lang, "tool.now") | ||||
| 		diffStr = lang.Tr("tool.now") | ||||
| 	case diff < 2: | ||||
| 		diff = 0 | ||||
| 		diffStr = i18n.Tr(lang, "tool.1s") | ||||
| 		diffStr = lang.Tr("tool.1s") | ||||
| 	case diff < 1*Minute: | ||||
| 		diffStr = i18n.Tr(lang, "tool.seconds", diff) | ||||
| 		diffStr = lang.Tr("tool.seconds", diff) | ||||
| 		diff = 0 | ||||
|  | ||||
| 	case diff < 2*Minute: | ||||
| 		diff -= 1 * Minute | ||||
| 		diffStr = i18n.Tr(lang, "tool.1m") | ||||
| 		diffStr = lang.Tr("tool.1m") | ||||
| 	case diff < 1*Hour: | ||||
| 		diffStr = i18n.Tr(lang, "tool.minutes", diff/Minute) | ||||
| 		diffStr = lang.Tr("tool.minutes", diff/Minute) | ||||
| 		diff -= diff / Minute * Minute | ||||
|  | ||||
| 	case diff < 2*Hour: | ||||
| 		diff -= 1 * Hour | ||||
| 		diffStr = i18n.Tr(lang, "tool.1h") | ||||
| 		diffStr = lang.Tr("tool.1h") | ||||
| 	case diff < 1*Day: | ||||
| 		diffStr = i18n.Tr(lang, "tool.hours", diff/Hour) | ||||
| 		diffStr = lang.Tr("tool.hours", diff/Hour) | ||||
| 		diff -= diff / Hour * Hour | ||||
|  | ||||
| 	case diff < 2*Day: | ||||
| 		diff -= 1 * Day | ||||
| 		diffStr = i18n.Tr(lang, "tool.1d") | ||||
| 		diffStr = lang.Tr("tool.1d") | ||||
| 	case diff < 1*Week: | ||||
| 		diffStr = i18n.Tr(lang, "tool.days", diff/Day) | ||||
| 		diffStr = lang.Tr("tool.days", diff/Day) | ||||
| 		diff -= diff / Day * Day | ||||
|  | ||||
| 	case diff < 2*Week: | ||||
| 		diff -= 1 * Week | ||||
| 		diffStr = i18n.Tr(lang, "tool.1w") | ||||
| 		diffStr = lang.Tr("tool.1w") | ||||
| 	case diff < 1*Month: | ||||
| 		diffStr = i18n.Tr(lang, "tool.weeks", diff/Week) | ||||
| 		diffStr = lang.Tr("tool.weeks", diff/Week) | ||||
| 		diff -= diff / Week * Week | ||||
|  | ||||
| 	case diff < 2*Month: | ||||
| 		diff -= 1 * Month | ||||
| 		diffStr = i18n.Tr(lang, "tool.1mon") | ||||
| 		diffStr = lang.Tr("tool.1mon") | ||||
| 	case diff < 1*Year: | ||||
| 		diffStr = i18n.Tr(lang, "tool.months", diff/Month) | ||||
| 		diffStr = lang.Tr("tool.months", diff/Month) | ||||
| 		diff -= diff / Month * Month | ||||
|  | ||||
| 	case diff < 2*Year: | ||||
| 		diff -= 1 * Year | ||||
| 		diffStr = i18n.Tr(lang, "tool.1y") | ||||
| 		diffStr = lang.Tr("tool.1y") | ||||
| 	default: | ||||
| 		diffStr = i18n.Tr(lang, "tool.years", diff/Year) | ||||
| 		diffStr = lang.Tr("tool.years", diff/Year) | ||||
| 		diff -= (diff / Year) * Year | ||||
| 	} | ||||
| 	return diff, diffStr | ||||
| } | ||||
|  | ||||
| func computeTimeDiff(diff int64, lang string) (int64, string) { | ||||
| 	var diffStr string | ||||
| func computeTimeDiff(diff int64, lang translation.Locale) (int64, string) { | ||||
| 	diffStr := "" | ||||
| 	switch { | ||||
| 	case diff <= 0: | ||||
| 		diff = 0 | ||||
| 		diffStr = i18n.Tr(lang, "tool.now") | ||||
| 		diffStr = lang.Tr("tool.now") | ||||
| 	case diff < 2: | ||||
| 		diff = 0 | ||||
| 		diffStr = i18n.Tr(lang, "tool.1s") | ||||
| 		diffStr = lang.Tr("tool.1s") | ||||
| 	case diff < 1*Minute: | ||||
| 		diffStr = i18n.Tr(lang, "tool.seconds", diff) | ||||
| 		diffStr = lang.Tr("tool.seconds", diff) | ||||
| 		diff = 0 | ||||
|  | ||||
| 	case diff < Minute+Minute/2: | ||||
| 		diff -= 1 * Minute | ||||
| 		diffStr = i18n.Tr(lang, "tool.1m") | ||||
| 		diffStr = lang.Tr("tool.1m") | ||||
| 	case diff < 1*Hour: | ||||
| 		minutes := round(float64(diff) / Minute) | ||||
| 		if minutes > 1 { | ||||
| 			diffStr = i18n.Tr(lang, "tool.minutes", minutes) | ||||
| 			diffStr = lang.Tr("tool.minutes", minutes) | ||||
| 		} else { | ||||
| 			diffStr = i18n.Tr(lang, "tool.1m") | ||||
| 			diffStr = lang.Tr("tool.1m") | ||||
| 		} | ||||
| 		diff -= diff / Minute * Minute | ||||
|  | ||||
| 	case diff < Hour+Hour/2: | ||||
| 		diff -= 1 * Hour | ||||
| 		diffStr = i18n.Tr(lang, "tool.1h") | ||||
| 		diffStr = lang.Tr("tool.1h") | ||||
| 	case diff < 1*Day: | ||||
| 		hours := round(float64(diff) / Hour) | ||||
| 		if hours > 1 { | ||||
| 			diffStr = i18n.Tr(lang, "tool.hours", hours) | ||||
| 			diffStr = lang.Tr("tool.hours", hours) | ||||
| 		} else { | ||||
| 			diffStr = i18n.Tr(lang, "tool.1h") | ||||
| 			diffStr = lang.Tr("tool.1h") | ||||
| 		} | ||||
| 		diff -= diff / Hour * Hour | ||||
|  | ||||
| 	case diff < Day+Day/2: | ||||
| 		diff -= 1 * Day | ||||
| 		diffStr = i18n.Tr(lang, "tool.1d") | ||||
| 		diffStr = lang.Tr("tool.1d") | ||||
| 	case diff < 1*Week: | ||||
| 		days := round(float64(diff) / Day) | ||||
| 		if days > 1 { | ||||
| 			diffStr = i18n.Tr(lang, "tool.days", days) | ||||
| 			diffStr = lang.Tr("tool.days", days) | ||||
| 		} else { | ||||
| 			diffStr = i18n.Tr(lang, "tool.1d") | ||||
| 			diffStr = lang.Tr("tool.1d") | ||||
| 		} | ||||
| 		diff -= diff / Day * Day | ||||
|  | ||||
| 	case diff < Week+Week/2: | ||||
| 		diff -= 1 * Week | ||||
| 		diffStr = i18n.Tr(lang, "tool.1w") | ||||
| 		diffStr = lang.Tr("tool.1w") | ||||
| 	case diff < 1*Month: | ||||
| 		weeks := round(float64(diff) / Week) | ||||
| 		if weeks > 1 { | ||||
| 			diffStr = i18n.Tr(lang, "tool.weeks", weeks) | ||||
| 			diffStr = lang.Tr("tool.weeks", weeks) | ||||
| 		} else { | ||||
| 			diffStr = i18n.Tr(lang, "tool.1w") | ||||
| 			diffStr = lang.Tr("tool.1w") | ||||
| 		} | ||||
| 		diff -= diff / Week * Week | ||||
|  | ||||
| 	case diff < 1*Month+Month/2: | ||||
| 		diff -= 1 * Month | ||||
| 		diffStr = i18n.Tr(lang, "tool.1mon") | ||||
| 		diffStr = lang.Tr("tool.1mon") | ||||
| 	case diff < 1*Year: | ||||
| 		months := round(float64(diff) / Month) | ||||
| 		if months > 1 { | ||||
| 			diffStr = i18n.Tr(lang, "tool.months", months) | ||||
| 			diffStr = lang.Tr("tool.months", months) | ||||
| 		} else { | ||||
| 			diffStr = i18n.Tr(lang, "tool.1mon") | ||||
| 			diffStr = lang.Tr("tool.1mon") | ||||
| 		} | ||||
| 		diff -= diff / Month * Month | ||||
|  | ||||
| 	case diff < Year+Year/2: | ||||
| 		diff -= 1 * Year | ||||
| 		diffStr = i18n.Tr(lang, "tool.1y") | ||||
| 		diffStr = lang.Tr("tool.1y") | ||||
| 	default: | ||||
| 		years := round(float64(diff) / Year) | ||||
| 		if years > 1 { | ||||
| 			diffStr = i18n.Tr(lang, "tool.years", years) | ||||
| 			diffStr = lang.Tr("tool.years", years) | ||||
| 		} else { | ||||
| 			diffStr = i18n.Tr(lang, "tool.1y") | ||||
| 			diffStr = lang.Tr("tool.1y") | ||||
| 		} | ||||
| 		diff -= (diff / Year) * Year | ||||
| 	} | ||||
| @@ -177,24 +177,24 @@ func computeTimeDiff(diff int64, lang string) (int64, string) { | ||||
|  | ||||
| // MinutesToFriendly returns a user friendly string with number of minutes | ||||
| // converted to hours and minutes. | ||||
| func MinutesToFriendly(minutes int, lang string) string { | ||||
| func MinutesToFriendly(minutes int, lang translation.Locale) string { | ||||
| 	duration := time.Duration(minutes) * time.Minute | ||||
| 	return TimeSincePro(time.Now().Add(-duration), lang) | ||||
| } | ||||
|  | ||||
| // TimeSincePro calculates the time interval and generate full user-friendly string. | ||||
| func TimeSincePro(then time.Time, lang string) string { | ||||
| func TimeSincePro(then time.Time, lang translation.Locale) string { | ||||
| 	return timeSincePro(then, time.Now(), lang) | ||||
| } | ||||
|  | ||||
| func timeSincePro(then, now time.Time, lang string) string { | ||||
| func timeSincePro(then, now time.Time, lang translation.Locale) string { | ||||
| 	diff := now.Unix() - then.Unix() | ||||
|  | ||||
| 	if then.After(now) { | ||||
| 		return i18n.Tr(lang, "tool.future") | ||||
| 		return lang.Tr("tool.future") | ||||
| 	} | ||||
| 	if diff == 0 { | ||||
| 		return i18n.Tr(lang, "tool.now") | ||||
| 		return lang.Tr("tool.now") | ||||
| 	} | ||||
|  | ||||
| 	var timeStr, diffStr string | ||||
| @@ -209,11 +209,11 @@ func timeSincePro(then, now time.Time, lang string) string { | ||||
| 	return strings.TrimPrefix(timeStr, ", ") | ||||
| } | ||||
|  | ||||
| func timeSince(then, now time.Time, lang string) string { | ||||
| func timeSince(then, now time.Time, lang translation.Locale) string { | ||||
| 	return timeSinceUnix(then.Unix(), now.Unix(), lang) | ||||
| } | ||||
|  | ||||
| func timeSinceUnix(then, now int64, lang string) string { | ||||
| func timeSinceUnix(then, now int64, lang translation.Locale) string { | ||||
| 	lbl := "tool.ago" | ||||
| 	diff := now - then | ||||
| 	if then > now { | ||||
| @@ -221,36 +221,31 @@ func timeSinceUnix(then, now int64, lang string) string { | ||||
| 		diff = then - now | ||||
| 	} | ||||
| 	if diff <= 0 { | ||||
| 		return i18n.Tr(lang, "tool.now") | ||||
| 		return lang.Tr("tool.now") | ||||
| 	} | ||||
|  | ||||
| 	_, diffStr := computeTimeDiff(diff, lang) | ||||
| 	return i18n.Tr(lang, lbl, diffStr) | ||||
| } | ||||
|  | ||||
| // RawTimeSince retrieves i18n key of time since t | ||||
| func RawTimeSince(t time.Time, lang string) string { | ||||
| 	return timeSince(t, time.Now(), lang) | ||||
| 	return lang.Tr(lbl, diffStr) | ||||
| } | ||||
|  | ||||
| // TimeSince calculates the time interval and generate user-friendly string. | ||||
| func TimeSince(then time.Time, lang string) template.HTML { | ||||
| func TimeSince(then time.Time, lang translation.Locale) template.HTML { | ||||
| 	return htmlTimeSince(then, time.Now(), lang) | ||||
| } | ||||
|  | ||||
| func htmlTimeSince(then, now time.Time, lang string) template.HTML { | ||||
| func htmlTimeSince(then, now time.Time, lang translation.Locale) template.HTML { | ||||
| 	return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, | ||||
| 		then.In(setting.DefaultUILocation).Format(GetTimeFormat(lang)), | ||||
| 		then.In(setting.DefaultUILocation).Format(GetTimeFormat(lang.Language())), | ||||
| 		timeSince(then, now, lang))) | ||||
| } | ||||
|  | ||||
| // TimeSinceUnix calculates the time interval and generate user-friendly string. | ||||
| func TimeSinceUnix(then TimeStamp, lang string) template.HTML { | ||||
| func TimeSinceUnix(then TimeStamp, lang translation.Locale) template.HTML { | ||||
| 	return htmlTimeSinceUnix(then, TimeStamp(time.Now().Unix()), lang) | ||||
| } | ||||
|  | ||||
| func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML { | ||||
| func htmlTimeSinceUnix(then, now TimeStamp, lang translation.Locale) template.HTML { | ||||
| 	return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, | ||||
| 		then.FormatInLocation(GetTimeFormat(lang), setting.DefaultUILocation), | ||||
| 		then.FormatInLocation(GetTimeFormat(lang.Language()), setting.DefaultUILocation), | ||||
| 		timeSinceUnix(int64(then), int64(now), lang))) | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,6 @@ import ( | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/setting" | ||||
| 	"code.gitea.io/gitea/modules/translation" | ||||
| 	"code.gitea.io/gitea/modules/translation/i18n" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| @@ -42,16 +41,16 @@ func TestMain(m *testing.M) { | ||||
| } | ||||
|  | ||||
| func TestTimeSince(t *testing.T) { | ||||
| 	assert.Equal(t, "now", timeSince(BaseDate, BaseDate, "en")) | ||||
| 	assert.Equal(t, "now", timeSince(BaseDate, BaseDate, translation.NewLocale("en-US"))) | ||||
|  | ||||
| 	// test that each diff in `diffs` yields the expected string | ||||
| 	test := func(expected string, diffs ...time.Duration) { | ||||
| 		t.Run(expected, func(t *testing.T) { | ||||
| 			for _, diff := range diffs { | ||||
| 				actual := timeSince(BaseDate, BaseDate.Add(diff), "en") | ||||
| 				assert.Equal(t, i18n.Tr("en", "tool.ago", expected), actual) | ||||
| 				actual = timeSince(BaseDate.Add(diff), BaseDate, "en") | ||||
| 				assert.Equal(t, i18n.Tr("en", "tool.from_now", expected), actual) | ||||
| 				actual := timeSince(BaseDate, BaseDate.Add(diff), translation.NewLocale("en-US")) | ||||
| 				assert.Equal(t, translation.NewLocale("en-US").Tr("tool.ago", expected), actual) | ||||
| 				actual = timeSince(BaseDate.Add(diff), BaseDate, translation.NewLocale("en-US")) | ||||
| 				assert.Equal(t, translation.NewLocale("en-US").Tr("tool.from_now", expected), actual) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| @@ -82,13 +81,13 @@ func TestTimeSince(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestTimeSincePro(t *testing.T) { | ||||
| 	assert.Equal(t, "now", timeSincePro(BaseDate, BaseDate, "en")) | ||||
| 	assert.Equal(t, "now", timeSincePro(BaseDate, BaseDate, translation.NewLocale("en-US"))) | ||||
|  | ||||
| 	// test that a difference of `diff` yields the expected string | ||||
| 	test := func(expected string, diff time.Duration) { | ||||
| 		actual := timeSincePro(BaseDate, BaseDate.Add(diff), "en") | ||||
| 		actual := timeSincePro(BaseDate, BaseDate.Add(diff), translation.NewLocale("en-US")) | ||||
| 		assert.Equal(t, expected, actual) | ||||
| 		assert.Equal(t, "future", timeSincePro(BaseDate.Add(diff), BaseDate, "en")) | ||||
| 		assert.Equal(t, "future", timeSincePro(BaseDate.Add(diff), BaseDate, translation.NewLocale("en-US"))) | ||||
| 	} | ||||
| 	test("1 second", time.Second) | ||||
| 	test("2 seconds", 2*time.Second) | ||||
| @@ -119,7 +118,7 @@ func TestHtmlTimeSince(t *testing.T) { | ||||
| 	setting.DefaultUILocation = time.UTC | ||||
| 	// test that `diff` yields a result containing `expected` | ||||
| 	test := func(expected string, diff time.Duration) { | ||||
| 		actual := htmlTimeSince(BaseDate, BaseDate.Add(diff), "en") | ||||
| 		actual := htmlTimeSince(BaseDate, BaseDate.Add(diff), translation.NewLocale("en-US")) | ||||
| 		assert.Contains(t, actual, `title="Sat Jan  1 00:00:00 UTC 2000"`) | ||||
| 		assert.Contains(t, actual, expected) | ||||
| 	} | ||||
| @@ -138,7 +137,7 @@ func TestComputeTimeDiff(t *testing.T) { | ||||
| 	test := func(base int64, str string, offsets ...int64) { | ||||
| 		for _, offset := range offsets { | ||||
| 			t.Run(fmt.Sprintf("%s:%d", str, offset), func(t *testing.T) { | ||||
| 				diff, diffStr := computeTimeDiff(base+offset, "en") | ||||
| 				diff, diffStr := computeTimeDiff(base+offset, translation.NewLocale("en-US")) | ||||
| 				assert.Equal(t, offset, diff) | ||||
| 				assert.Equal(t, str, diffStr) | ||||
| 			}) | ||||
| @@ -171,7 +170,7 @@ func TestComputeTimeDiff(t *testing.T) { | ||||
| func TestMinutesToFriendly(t *testing.T) { | ||||
| 	// test that a number of minutes yields the expected string | ||||
| 	test := func(expected string, minutes int) { | ||||
| 		actual := MinutesToFriendly(minutes, "en") | ||||
| 		actual := MinutesToFriendly(minutes, translation.NewLocale("en-US")) | ||||
| 		assert.Equal(t, expected, actual) | ||||
| 	} | ||||
| 	test("1 minute", 1) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user