Skip to content

Commit

Permalink
Localize hard-coded texts (#2044)
Browse files Browse the repository at this point in the history
* feat(lang): localize some views

* feat(lang): an attempt at localizing vuetify (WIP)

* feat(lang): localized some more screens

* feat(lang): localized some more screens again

* feat(lang): hack to localize vuetify

* feat(lang): localize data management pages

* fix linting errors

---------

Co-authored-by: Hayden <[email protected]>
  • Loading branch information
sephrat and hay-kot authored Jan 29, 2023
1 parent 754d4c3 commit f8b8680
Show file tree
Hide file tree
Showing 55 changed files with 695 additions and 393 deletions.
8 changes: 5 additions & 3 deletions frontend/components/Domain/Group/GroupMealPlanRuleForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
<RecipeOrganizerSelector v-model="inputCategories" selector-type="categories" />
<RecipeOrganizerSelector v-model="inputTags" selector-type="tags" />

<!-- TODO Make this localizable -->
{{ inputDay === "unset" ? "This rule will apply to all days" : `This rule applies on ${inputDay}s` }}
{{ inputEntryType === "unset" ? "for all meal types" : ` and for ${inputEntryType} meal types` }}
<!-- TODO: proper pluralization of inputDay -->
{{ $t('meal-plan.this-rule-will-apply', {
dayCriteria: inputDay === "unset" ? $t('meal-plan.to-all-days') : $t('meal-plan.on-days', [inputDay]),
mealTypeCriteria: inputEntryType === "unset" ? $t('meal-plan.for-all-meal-types') : $t('meal-plan.for-type-meal-types', [inputEntryType])
}) }}
</div>
</template>

Expand Down
20 changes: 10 additions & 10 deletions frontend/components/Domain/Group/GroupPreferencesEditor.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div v-if="preferences">
<BaseCardSectionTitle title="General Preferences"></BaseCardSectionTitle>
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" label="Private Group"></v-checkbox>
<BaseCardSectionTitle :title="$tc('group.general-preferences')"></BaseCardSectionTitle>
<v-checkbox v-model="preferences.privateGroup" class="mt-n4" :label="$t('group.private-group')"></v-checkbox>
<v-select
v-model="preferences.firstDayOfWeek"
:prepend-icon="$globals.icons.calendarWeekBegin"
Expand All @@ -11,7 +11,7 @@
:label="$t('settings.first-day-of-week')"
/>

<BaseCardSectionTitle class="mt-5" title="Group Recipe Preferences"></BaseCardSectionTitle>
<BaseCardSectionTitle class="mt-5" :title="$tc('group.group-recipe-preferences')"></BaseCardSectionTitle>
<template v-for="(_, key) in preferences">
<v-checkbox
v-if="labels[key]"
Expand All @@ -38,12 +38,12 @@ export default defineComponent({
const { i18n } = useContext();
const labels = {
recipePublic: "Allow users outside of your group to see your recipes",
recipeShowNutrition: "Show nutrition information",
recipeShowAssets: "Show recipe assets",
recipeLandscapeView: "Default to landscape view",
recipeDisableComments: "Disable recipe comments from users in your group",
recipeDisableAmount: "Disable organizing recipe ingredients by units and food",
recipePublic: i18n.tc("group.allow-users-outside-of-your-group-to-see-your-recipes"),
recipeShowNutrition: i18n.tc("group.show-nutrition-information"),
recipeShowAssets: i18n.tc("group.show-recipe-assets"),
recipeLandscapeView: i18n.tc("group.default-to-landscape-view"),
recipeDisableComments: i18n.tc("group.disable-users-from-commenting-on-recipes"),
recipeDisableAmount: i18n.tc("group.disable-organizing-recipe-ingredients-by-units-and-food"),
};
const allDays = [
Expand Down Expand Up @@ -96,4 +96,4 @@ export default defineComponent({
</script>

<style lang="scss" scoped>
</style>
</style>
2 changes: 1 addition & 1 deletion frontend/components/Domain/Recipe/RecipeCardSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<v-icon left>
{{ $globals.icons.chefHat }}
</v-icon>
<v-list-item-title>{{ "Last Made" }}</v-list-item-title>
<v-list-item-title>{{ $t('general.last-made') }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/Domain/Recipe/RecipeLastMade.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
<div class="d-flex justify-center flex-wrap">
<BaseButton :small="$vuetify.breakpoint.smAndDown" @click="madeThisDialog = true">
<template #icon> {{ $globals.icons.chefHat }} </template>
I Made This
{{ $t('recipe.made-this') }}
</BaseButton>
</div>
<div class="d-flex justify-center flex-wrap">
Expand All @@ -63,7 +63,7 @@
<v-icon left>
{{ $globals.icons.calendar }}
</v-icon>
Last Made {{ value ? new Date(value+"Z").toLocaleDateString($i18n.locale) : $t("general.never") }}
{{ $t('recipe.last-made-date', { date: value ? new Date(value+"Z").toLocaleDateString($i18n.locale) : $t("general.never") } ) }}
</v-chip>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@
</v-card-actions>
<AdvancedOnly>
<v-card v-if="isEditForm" flat class="ma-2 mb-2">
<v-card-title> API Extras </v-card-title>
<v-card-title> {{ $t('recipe.api-extras') }} </v-card-title>
<v-divider class="mx-2"></v-divider>
<v-card-text>
Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs
within a recipe to reference from 3rd part applications. You can use these keys to contain information to
trigger automation or custom messages to relay to your desired device.
{{ $t('recipe.api-extras-description') }}
<v-row v-for="(_, key) in recipe.extras" :key="key" class="mt-1">
<v-col cols="8">
<v-text-field v-model="recipe.extras[key]" dense :label="key">
Expand All @@ -45,7 +43,7 @@
</v-card-text>
<v-card-actions class="d-flex">
<div style="max-width: 200px">
<v-text-field v-model="apiNewKey" label="Message Key"></v-text-field>
<v-text-field v-model="apiNewKey" :label="$t('recipe.message-key')"></v-text-field>
</div>
<BaseButton create small class="ml-5" @click="createApiExtra" />
</v-card-actions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<template #icon>
{{ $globals.icons.foods }}
</template>
Parse
{{ $t('recipe.parse') }}
</BaseButton>
</span>
</template>
Expand All @@ -53,7 +53,7 @@

<script lang="ts">
import draggable from "vuedraggable";
import { computed, defineComponent, ref } from "@nuxtjs/composition-api";
import { computed, defineComponent, ref, useContext } from "@nuxtjs/composition-api";
import { usePageState, usePageUser } from "~/composables/recipe-page/shared-state";
import { NoUndefinedField } from "~/lib/api/types/non-generated";
import { Recipe } from "~/lib/api/types/recipe";
Expand All @@ -75,6 +75,7 @@ export default defineComponent({
setup(props) {
const { user } = usePageUser();
const { imageKey } = usePageState(props.recipe.slug);
const { i18n } = useContext();
const drag = ref(false);
Expand All @@ -95,11 +96,11 @@ export default defineComponent({
const parserToolTip = computed(() => {
if (props.recipe.settings.disableAmount) {
return "Enable ingredient amounts to use this feature";
return i18n.t("recipe.enable-ingredient-amounts-to-use-this-feature");
} else if (hasFoodOrUnit.value) {
return "Recipes with units or foods defined cannot be parsed.";
return i18n.t("recipe.recipes-with-units-or-foods-defined-cannot-be-parsed");
}
return "Parse ingredients";
return i18n.t("recipe.parse-ingredients");
});
function addIngredient(ingredients: Array<string> | null = null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
:disable-amount="recipe.settings.disableAmount"
/>
<div v-if="!isEditMode && recipe.tools && recipe.tools.length > 0">
<h2 class="mb-2 mt-4">Required Tools</h2>
<h2 class="mb-2 mt-4">{{ $t('tool.required-tools') }}</h2>
<v-list-item v-for="(tool, index) in recipe.tools" :key="index" dense>
<v-checkbox
v-model="recipe.tools[index].onHand"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,15 @@
event: 'open',
children: [
{
text: 'Toggle Section',
text: $tc('recipe.toggle-section'),
event: 'toggle-section',
},
{
text: 'Link Ingredients',
text: $tc('recipe.link-ingredients'),
event: 'link-ingredients',
},
{
text: 'Merge Above',
text: $tc('recipe.merge-above'),
event: 'merge-above',
},
{
Expand All @@ -152,7 +152,7 @@
},
{
icon: previewStates[index] ? $globals.icons.edit : $globals.icons.eye,
text: previewStates[index] ? 'Edit Markdown' : 'Preview Markdown',
text: previewStates[index] ? $tc('recipe.edit-markdown') : $tc('markdown-editor.preview-markdown-button-label'),
event: 'preview-step',
},
],
Expand Down Expand Up @@ -188,7 +188,7 @@
:preview.sync="previewStates[index]"
:display-preview="false"
:textarea="{
hint: 'Attach images by dragging & dropping them into the editor',
hint: $t('recipe.attach-images-hint'),
persistentHint: true,
}"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

<!-- Recipe Tools Edit -->
<v-card v-if="isEditForm" class="mt-2">
<v-card-title class="py-2"> Required Tools </v-card-title>
<v-card-title class="py-2"> {{ $t('tool.required-tools') }} </v-card-title>
<v-divider class="mx-2" />
<v-card-text class="pt-0">
<RecipeOrganizerSelector v-model="recipe.tools" selector-type="tools" />
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/Layout/TheSnackbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{{ toastAlert.text }}

<template #action="{ attrs }">
<v-btn text v-bind="attrs" @click="toastAlert.open = false"> Close </v-btn>
<v-btn text v-bind="attrs" @click="toastAlert.open = false"> {{ $t('general.close') }} </v-btn>
</template>
</v-snackbar>
<v-snackbar
Expand Down
7 changes: 5 additions & 2 deletions frontend/components/global/BaseDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ export default defineComponent({
},
submitText: {
type: String,
// TODO Figure out how to localize this default value
default: () => "Create",
default: function () {
return this.$t("general.create");
}
},
keepOpen: {
default: false,
Expand All @@ -117,6 +118,8 @@ export default defineComponent({
setup(props, context) {
const dialog = computed<boolean>({
get() {
// @ts-expect-error - props inference doesn't work here for some reason
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return props.value;
},
set(val) {
Expand Down
5 changes: 3 additions & 2 deletions frontend/components/global/BaseOverflowButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ export default defineComponent({
btnText: {
type: String,
required: false,
// TODO Figure out how to localize this default value
default: "Actions",
default: function () {
return this.$t("general.actions");
}
},
},
setup(props, context) {
Expand Down
11 changes: 7 additions & 4 deletions frontend/composables/use-copy.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
import { useContext } from "@nuxtjs/composition-api";
import { useClipboard } from "@vueuse/core";
import { alert } from "./use-toast";

export function useCopy() {
const { copy, copied, isSupported } = useClipboard();
const { i18n } = useContext();

function copyText(text: string) {
if (!isSupported) {
alert.error("Clipboard not supported");
alert.error(i18n.tc("general.clipboard-not-supported"));
return;
}
copy(text);
alert.success("Copied to clipboard");
alert.success(i18n.tc("general.copied-to-clipboard"));
}

return { copyText, copied };
}

export function useCopyList() {
const { copy, isSupported } = useClipboard();
const { i18n } = useContext();

function checkClipboard() {
if (!isSupported) {
alert.error("Your browser does not support clipboard");
alert.error(i18n.tc("general.your-browser-does-not-support-clipboard"));
return false;
}

Expand Down Expand Up @@ -51,7 +54,7 @@ export function useCopyList() {

function copyText(text: string, len: number) {
copy(text).then(() => {
alert.success(`Copied ${len} items to clipboard`);
alert.success(i18n.tc("general.copied-items-to-clipboard", len));
});
}

Expand Down
6 changes: 4 additions & 2 deletions frontend/composables/use-group-cookbooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useAsync, ref, Ref } from "@nuxtjs/composition-api";
import { useAsync, ref, Ref, useContext } from "@nuxtjs/composition-api";
import { useAsyncKey } from "./use-utils";
import { useUserApi } from "~/composables/api";
import { ReadCookBook, UpdateCookBook } from "~/lib/api/types/cookbook";
Expand All @@ -25,6 +25,8 @@ export const useCookbooks = function () {
const api = useUserApi();
const loading = ref(false);

const { i18n } = useContext();

const actions = {
getAll() {
loading.value = true;
Expand Down Expand Up @@ -54,7 +56,7 @@ export const useCookbooks = function () {
async createOne() {
loading.value = true;
const { data } = await api.cookbooks.createOne({
name: "Cookbook " + String((cookbookStore?.value?.length ?? 0) + 1),
name: i18n.t("cookbook.cookbook-with-name", [String((cookbookStore?.value?.length ?? 0) + 1)]) as string,
});
if (data && cookbookStore?.value) {
cookbookStore.value.push(data);
Expand Down
5 changes: 4 additions & 1 deletion frontend/composables/use-locales/use-locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { computed, useContext } from "@nuxtjs/composition-api";
import { LOCALES } from "./available-locales";

export const useLocales = () => {
const { i18n } = useContext();
const { i18n, $vuetify } = useContext();

const locale = computed<string>({
get() {
$vuetify.lang.current = i18n.locale; // dirty hack
return i18n.locale;
},
set(value) {
i18n.setLocale(value);
$vuetify.lang.current = value; // this does not persist after window reload :-(

// Reload the page to update the language - not all strings are reactive
window.location.reload();
},
Expand Down
11 changes: 7 additions & 4 deletions frontend/composables/use-passwords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,22 @@ export function usePasswordField() {
}

export const usePasswordStrength = (password: Ref<string>) => {
const { i18n } = useContext();

const score = computed(() => {
return scorePassword(password.value);
});


const strength = computed(() => {
if (score.value < 50) {
return "Weak";
return i18n.tc("user.password-strength-values.weak");
} else if (score.value < 80) {
return "Good";
return i18n.tc("user.password-strength-values.good");
} else if (score.value < 100) {
return "Strong";
return i18n.tc("user.password-strength-values.strong");
} else {
return "Very Strong";
return i18n.tc("user.password-strength-values.very-strong");
}
});

Expand Down
Loading

0 comments on commit f8b8680

Please sign in to comment.